password_store_mac.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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 <vector>
12
13#include "base/callback.h"
14#include "base/logging.h"
15#include "base/mac/mac_logging.h"
16#include "base/mac/mac_util.h"
17#include "base/message_loop.h"
18#include "base/stl_util.h"
19#include "base/string_util.h"
20#include "base/utf_string_conversions.h"
21#include "chrome/browser/password_manager/login_database.h"
22#include "chrome/browser/password_manager/password_store_change.h"
23#include "chrome/common/chrome_notification_types.h"
24#include "content/public/browser/notification_service.h"
25#include "crypto/apple_keychain.h"
26
27using crypto::AppleKeychain;
28using content::PasswordForm;
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  DCHECK(form);
238
239  SecKeychainAttributeInfo attrInfo;
240  UInt32 tags[] = { kSecAccountItemAttr,
241                    kSecServerItemAttr,
242                    kSecPortItemAttr,
243                    kSecPathItemAttr,
244                    kSecProtocolItemAttr,
245                    kSecAuthenticationTypeItemAttr,
246                    kSecSecurityDomainItemAttr,
247                    kSecCreationDateItemAttr,
248                    kSecNegativeItemAttr };
249  attrInfo.count = arraysize(tags);
250  attrInfo.tag = tags;
251  attrInfo.format = NULL;
252
253  SecKeychainAttributeList *attrList;
254  UInt32 password_length;
255  void* password_data;
256  OSStatus result = keychain.ItemCopyAttributesAndData(keychain_item, &attrInfo,
257                                                       NULL, &attrList,
258                                                       &password_length,
259                                                       &password_data);
260
261  if (result != noErr) {
262    // We don't log errSecAuthFailed because that just means that the user
263    // chose not to allow us access to the item.
264    if (result != errSecAuthFailed) {
265      OSSTATUS_LOG(ERROR, result) << "Keychain data load failed";
266    }
267    return false;
268  }
269
270  UTF8ToUTF16(static_cast<const char *>(password_data), password_length,
271              &(form->password_value));
272
273  int port = kAnyPort;
274  std::string server;
275  std::string security_domain;
276  std::string path;
277  for (unsigned int i = 0; i < attrList->count; i++) {
278    SecKeychainAttribute attr = attrList->attr[i];
279    if (!attr.data) {
280      continue;
281    }
282    switch (attr.tag) {
283      case kSecAccountItemAttr:
284        UTF8ToUTF16(static_cast<const char *>(attr.data), attr.length,
285                    &(form->username_value));
286        break;
287      case kSecServerItemAttr:
288        server.assign(static_cast<const char *>(attr.data), attr.length);
289        break;
290      case kSecPortItemAttr:
291        port = *(static_cast<UInt32*>(attr.data));
292        break;
293      case kSecPathItemAttr:
294        path.assign(static_cast<const char *>(attr.data), attr.length);
295        break;
296      case kSecProtocolItemAttr:
297      {
298        SecProtocolType protocol = *(static_cast<SecProtocolType*>(attr.data));
299        // TODO(stuartmorgan): Handle proxy types
300        form->ssl_valid = (protocol == kSecProtocolTypeHTTPS);
301        break;
302      }
303      case kSecAuthenticationTypeItemAttr:
304      {
305        SecAuthenticationType auth_type =
306            *(static_cast<SecAuthenticationType*>(attr.data));
307        form->scheme = SchemeForAuthType(auth_type);
308        break;
309      }
310      case kSecSecurityDomainItemAttr:
311        security_domain.assign(static_cast<const char *>(attr.data),
312                               attr.length);
313        break;
314      case kSecCreationDateItemAttr:
315        // The only way to get a date out of Keychain is as a string. Really.
316        // (The docs claim it's an int, but the header is correct.)
317        TimeFromKeychainTimeString(static_cast<char*>(attr.data), attr.length,
318                                   &form->date_created);
319        break;
320      case kSecNegativeItemAttr:
321        Boolean negative_item = *(static_cast<Boolean*>(attr.data));
322        if (negative_item) {
323          form->blacklisted_by_user = true;
324        }
325        break;
326    }
327  }
328  keychain.ItemFreeAttributesAndData(attrList, password_data);
329
330  // kSecNegativeItemAttr doesn't seem to actually be in widespread use. In
331  // practice, other browsers seem to use a "" or " " password (and a special
332  // user name) to indicated blacklist entries.
333  if (form->password_value.empty() || EqualsASCII(form->password_value, " ")) {
334    form->blacklisted_by_user = true;
335  }
336
337  form->origin = URLFromComponents(form->ssl_valid, server, port, path);
338  // TODO(stuartmorgan): Handle proxies, which need a different signon_realm
339  // format.
340  form->signon_realm = form->origin.GetOrigin().spec();
341  if (form->scheme != PasswordForm::SCHEME_HTML) {
342    form->signon_realm.append(security_domain);
343  }
344  return true;
345}
346
347bool FormsMatchForMerge(const PasswordForm& form_a,
348                        const PasswordForm& form_b) {
349  // We never merge blacklist entries between our store and the keychain.
350  if (form_a.blacklisted_by_user || form_b.blacklisted_by_user) {
351    return false;
352  }
353  return form_a.scheme == form_b.scheme &&
354         form_a.signon_realm == form_b.signon_realm &&
355         form_a.username_value == form_b.username_value;
356}
357
358// Returns an the best match for |form| from |keychain_forms|, or NULL if there
359// is no suitable match.
360PasswordForm* BestKeychainFormForForm(
361    const PasswordForm& base_form,
362    const std::vector<PasswordForm*>* keychain_forms) {
363  PasswordForm* partial_match = NULL;
364  for (std::vector<PasswordForm*>::const_iterator i = keychain_forms->begin();
365       i != keychain_forms->end(); ++i) {
366    // TODO(stuartmorgan): We should really be scoring path matches and picking
367    // the best, rather than just checking exact-or-not (although in practice
368    // keychain items with paths probably came from us).
369    if (FormsMatchForMerge(base_form, *(*i))) {
370      if (base_form.origin == (*i)->origin) {
371        return *i;
372      } else if (!partial_match) {
373        partial_match = *i;
374      }
375    }
376  }
377  return partial_match;
378}
379
380// Returns entries from |forms| that are blacklist entries, after removing
381// them from |forms|.
382std::vector<PasswordForm*> ExtractBlacklistForms(
383    std::vector<PasswordForm*>* forms) {
384  std::vector<PasswordForm*> blacklist_forms;
385  for (std::vector<PasswordForm*>::iterator i = forms->begin();
386       i != forms->end();) {
387    PasswordForm* form = *i;
388    if (form->blacklisted_by_user) {
389      blacklist_forms.push_back(form);
390      i = forms->erase(i);
391    } else {
392      ++i;
393    }
394  }
395  return blacklist_forms;
396}
397
398// Deletes and removes from v any element that exists in s.
399template <class T>
400void DeleteVectorElementsInSet(std::vector<T*>* v, const std::set<T*>& s) {
401  for (typename std::vector<T*>::iterator i = v->begin(); i != v->end();) {
402    T* element = *i;
403    if (s.find(element) != s.end()) {
404      delete element;
405      i = v->erase(i);
406    } else {
407      ++i;
408    }
409  }
410}
411
412void MergePasswordForms(std::vector<PasswordForm*>* keychain_forms,
413                        std::vector<PasswordForm*>* database_forms,
414                        std::vector<PasswordForm*>* merged_forms) {
415  // Pull out the database blacklist items, since they are used as-is rather
416  // than being merged with keychain forms.
417  std::vector<PasswordForm*> database_blacklist_forms =
418      ExtractBlacklistForms(database_forms);
419
420  // Merge the normal entries.
421  std::set<PasswordForm*> used_keychain_forms;
422  for (std::vector<PasswordForm*>::iterator i = database_forms->begin();
423       i != database_forms->end();) {
424    PasswordForm* db_form = *i;
425    PasswordForm* best_match = BestKeychainFormForForm(*db_form,
426                                                       keychain_forms);
427    if (best_match) {
428      used_keychain_forms.insert(best_match);
429      db_form->password_value = best_match->password_value;
430      merged_forms->push_back(db_form);
431      i = database_forms->erase(i);
432    } else {
433      ++i;
434    }
435  }
436
437  // Add in the blacklist entries from the database.
438  merged_forms->insert(merged_forms->end(),
439                       database_blacklist_forms.begin(),
440                       database_blacklist_forms.end());
441
442  // Clear out all the Keychain entries we used.
443  DeleteVectorElementsInSet(keychain_forms, used_keychain_forms);
444}
445
446std::vector<PasswordForm*> GetPasswordsForForms(
447    const AppleKeychain& keychain,
448    std::vector<PasswordForm*>* database_forms) {
449  MacKeychainPasswordFormAdapter keychain_adapter(&keychain);
450
451  std::vector<PasswordForm*> merged_forms;
452  for (std::vector<PasswordForm*>::iterator i = database_forms->begin();
453       i != database_forms->end();) {
454    std::vector<PasswordForm*> db_form_container(1, *i);
455    std::vector<PasswordForm*> keychain_matches =
456        keychain_adapter.PasswordsMergeableWithForm(**i);
457    MergePasswordForms(&keychain_matches, &db_form_container, &merged_forms);
458    if (db_form_container.empty()) {
459      i = database_forms->erase(i);
460    } else {
461      ++i;
462    }
463    STLDeleteElements(&keychain_matches);
464  }
465  return merged_forms;
466}
467
468}  // namespace internal_keychain_helpers
469
470#pragma mark -
471
472MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter(
473    const AppleKeychain* keychain)
474    : keychain_(keychain), finds_only_owned_(false) {
475}
476
477std::vector<PasswordForm*>
478    MacKeychainPasswordFormAdapter::PasswordsFillingForm(
479        const PasswordForm& query_form) {
480  std::vector<SecKeychainItemRef> keychain_items =
481      MatchingKeychainItems(query_form.signon_realm, query_form.scheme,
482                            NULL, NULL);
483
484  return ConvertKeychainItemsToForms(&keychain_items);
485}
486
487std::vector<PasswordForm*>
488    MacKeychainPasswordFormAdapter::PasswordsMergeableWithForm(
489        const PasswordForm& query_form) {
490  std::string username = UTF16ToUTF8(query_form.username_value);
491  std::vector<SecKeychainItemRef> keychain_items =
492      MatchingKeychainItems(query_form.signon_realm, query_form.scheme,
493                            NULL, username.c_str());
494
495  return ConvertKeychainItemsToForms(&keychain_items);
496}
497
498PasswordForm* MacKeychainPasswordFormAdapter::PasswordExactlyMatchingForm(
499    const PasswordForm& query_form) {
500  SecKeychainItemRef keychain_item = KeychainItemForForm(query_form);
501  if (keychain_item) {
502    PasswordForm* form = new PasswordForm();
503    internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_,
504                                                                keychain_item,
505                                                                form);
506    keychain_->Free(keychain_item);
507    return form;
508  }
509  return NULL;
510}
511
512bool MacKeychainPasswordFormAdapter::HasPasswordsMergeableWithForm(
513    const PasswordForm& query_form) {
514  std::string username = UTF16ToUTF8(query_form.username_value);
515  std::vector<SecKeychainItemRef> matches =
516      MatchingKeychainItems(query_form.signon_realm, query_form.scheme,
517                            NULL, username.c_str());
518  for (std::vector<SecKeychainItemRef>::iterator i = matches.begin();
519       i != matches.end(); ++i) {
520    keychain_->Free(*i);
521  }
522
523  return !matches.empty();
524}
525
526std::vector<PasswordForm*>
527    MacKeychainPasswordFormAdapter::GetAllPasswordFormPasswords() {
528  SecAuthenticationType supported_auth_types[] = {
529    kSecAuthenticationTypeHTMLForm,
530    kSecAuthenticationTypeHTTPBasic,
531    kSecAuthenticationTypeHTTPDigest,
532  };
533
534  std::vector<SecKeychainItemRef> matches;
535  for (unsigned int i = 0; i < arraysize(supported_auth_types); ++i) {
536    KeychainSearch keychain_search(*keychain_);
537    keychain_search.Init(NULL, 0, kSecProtocolTypeAny, supported_auth_types[i],
538                         NULL, NULL, NULL, CreatorCodeForSearch());
539    keychain_search.FindMatchingItems(&matches);
540  }
541
542  return ConvertKeychainItemsToForms(&matches);
543}
544
545bool MacKeychainPasswordFormAdapter::AddPassword(const PasswordForm& form) {
546  // We should never be trying to store a blacklist in the keychain.
547  DCHECK(!form.blacklisted_by_user);
548
549  std::string server;
550  std::string security_domain;
551  int port;
552  bool is_secure;
553  if (!ExtractSignonRealmComponents(form.signon_realm, &server, &port,
554                                    &is_secure, &security_domain)) {
555    return false;
556  }
557  std::string username = UTF16ToUTF8(form.username_value);
558  std::string password = UTF16ToUTF8(form.password_value);
559  std::string path = form.origin.path();
560  SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
561                                       : kSecProtocolTypeHTTP;
562  SecKeychainItemRef new_item = NULL;
563  OSStatus result = keychain_->AddInternetPassword(
564      NULL, server.size(), server.c_str(),
565      security_domain.size(), security_domain.c_str(),
566      username.size(), username.c_str(),
567      path.size(), path.c_str(),
568      port, protocol, AuthTypeForScheme(form.scheme),
569      password.size(), password.c_str(), &new_item);
570
571  if (result == noErr) {
572    SetKeychainItemCreatorCode(new_item,
573                               base::mac::CreatorCodeForApplication());
574    keychain_->Free(new_item);
575  } else if (result == errSecDuplicateItem) {
576    // If we collide with an existing item, find and update it instead.
577    SecKeychainItemRef existing_item = KeychainItemForForm(form);
578    if (!existing_item) {
579      return false;
580    }
581    bool changed = SetKeychainItemPassword(existing_item, password);
582    keychain_->Free(existing_item);
583    return changed;
584  }
585
586  return result == noErr;
587}
588
589bool MacKeychainPasswordFormAdapter::RemovePassword(const PasswordForm& form) {
590  SecKeychainItemRef keychain_item = KeychainItemForForm(form);
591  if (keychain_item == NULL)
592    return false;
593  OSStatus result = keychain_->ItemDelete(keychain_item);
594  keychain_->Free(keychain_item);
595  return result == noErr;
596}
597
598void MacKeychainPasswordFormAdapter::SetFindsOnlyOwnedItems(
599    bool finds_only_owned) {
600  finds_only_owned_ = finds_only_owned;
601}
602
603std::vector<PasswordForm*>
604    MacKeychainPasswordFormAdapter::ConvertKeychainItemsToForms(
605        std::vector<SecKeychainItemRef>* items) {
606  std::vector<PasswordForm*> keychain_forms;
607  for (std::vector<SecKeychainItemRef>::const_iterator i = items->begin();
608       i != items->end(); ++i) {
609    PasswordForm* form = new PasswordForm();
610    if (internal_keychain_helpers::FillPasswordFormFromKeychainItem(*keychain_,
611                                                                    *i, form)) {
612      keychain_forms.push_back(form);
613    }
614    keychain_->Free(*i);
615  }
616  items->clear();
617  return keychain_forms;
618}
619
620SecKeychainItemRef MacKeychainPasswordFormAdapter::KeychainItemForForm(
621    const PasswordForm& form) {
622  // We don't store blacklist entries in the keychain, so the answer to "what
623  // Keychain item goes with this form" is always "nothing" for blacklists.
624  if (form.blacklisted_by_user) {
625    return NULL;
626  }
627
628  std::string path = form.origin.path();
629  std::string username = UTF16ToUTF8(form.username_value);
630  std::vector<SecKeychainItemRef> matches = MatchingKeychainItems(
631      form.signon_realm, form.scheme, path.c_str(), username.c_str());
632
633  if (matches.empty()) {
634    return NULL;
635  }
636  // Free all items after the first, since we won't be returning them.
637  for (std::vector<SecKeychainItemRef>::iterator i = matches.begin() + 1;
638       i != matches.end(); ++i) {
639    keychain_->Free(*i);
640  }
641  return matches[0];
642}
643
644std::vector<SecKeychainItemRef>
645    MacKeychainPasswordFormAdapter::MatchingKeychainItems(
646        const std::string& signon_realm,
647        content::PasswordForm::Scheme scheme,
648        const char* path, const char* username) {
649  std::vector<SecKeychainItemRef> matches;
650
651  std::string server;
652  std::string security_domain;
653  int port;
654  bool is_secure;
655  if (!ExtractSignonRealmComponents(signon_realm, &server, &port,
656                                    &is_secure, &security_domain)) {
657    // TODO(stuartmorgan): Proxies will currently fail here, since their
658    // signon_realm is not a URL. We need to detect the proxy case and handle
659    // it specially.
660    return matches;
661  }
662  SecProtocolType protocol = is_secure ? kSecProtocolTypeHTTPS
663                                       : kSecProtocolTypeHTTP;
664  SecAuthenticationType auth_type = AuthTypeForScheme(scheme);
665  const char* auth_domain = (scheme == PasswordForm::SCHEME_HTML) ?
666      NULL : security_domain.c_str();
667  KeychainSearch keychain_search(*keychain_);
668  keychain_search.Init(server.c_str(), port, protocol, auth_type,
669                       auth_domain, path, username, CreatorCodeForSearch());
670  keychain_search.FindMatchingItems(&matches);
671  return matches;
672}
673
674// TODO(stuartmorgan): signon_realm for proxies is not yet supported.
675bool MacKeychainPasswordFormAdapter::ExtractSignonRealmComponents(
676    const std::string& signon_realm, std::string* server, int* port,
677    bool* is_secure, std::string* security_domain) {
678  // The signon_realm will be the Origin portion of a URL for an HTML form,
679  // and the same but with the security domain as a path for HTTP auth.
680  GURL realm_as_url(signon_realm);
681  if (!realm_as_url.is_valid()) {
682    return false;
683  }
684
685  if (server)
686    *server = realm_as_url.host();
687  if (is_secure)
688    *is_secure = realm_as_url.SchemeIsSecure();
689  if (port)
690    *port = realm_as_url.has_port() ? atoi(realm_as_url.port().c_str()) : 0;
691  if (security_domain) {
692    // Strip the leading '/' off of the path to get the security domain.
693    if (realm_as_url.path().length() > 0)
694      *security_domain = realm_as_url.path().substr(1);
695    else
696      security_domain->clear();
697  }
698  return true;
699}
700
701// Returns the Keychain SecAuthenticationType type corresponding to |scheme|.
702SecAuthenticationType MacKeychainPasswordFormAdapter::AuthTypeForScheme(
703    PasswordForm::Scheme scheme) {
704  switch (scheme) {
705    case PasswordForm::SCHEME_HTML:   return kSecAuthenticationTypeHTMLForm;
706    case PasswordForm::SCHEME_BASIC:  return kSecAuthenticationTypeHTTPBasic;
707    case PasswordForm::SCHEME_DIGEST: return kSecAuthenticationTypeHTTPDigest;
708    case PasswordForm::SCHEME_OTHER:  return kSecAuthenticationTypeDefault;
709  }
710  NOTREACHED();
711  return kSecAuthenticationTypeDefault;
712}
713
714bool MacKeychainPasswordFormAdapter::SetKeychainItemPassword(
715    const SecKeychainItemRef& keychain_item, const std::string& password) {
716  OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item, NULL,
717                                                           password.size(),
718                                                           password.c_str());
719  return result == noErr;
720}
721
722bool MacKeychainPasswordFormAdapter::SetKeychainItemCreatorCode(
723    const SecKeychainItemRef& keychain_item, OSType creator_code) {
724  SecKeychainAttribute attr = { kSecCreatorItemAttr, sizeof(creator_code),
725                                &creator_code };
726  SecKeychainAttributeList attrList = { 1, &attr };
727  OSStatus result = keychain_->ItemModifyAttributesAndData(keychain_item,
728                                                           &attrList, 0, NULL);
729  return result == noErr;
730}
731
732OSType MacKeychainPasswordFormAdapter::CreatorCodeForSearch() {
733  return finds_only_owned_ ? base::mac::CreatorCodeForApplication() : 0;
734}
735
736#pragma mark -
737
738PasswordStoreMac::PasswordStoreMac(AppleKeychain* keychain,
739                                   LoginDatabase* login_db)
740    : keychain_(keychain), login_metadata_db_(login_db) {
741  DCHECK(keychain_.get());
742  DCHECK(login_metadata_db_.get());
743}
744
745PasswordStoreMac::~PasswordStoreMac() {
746  if (thread_.get()) {
747    thread_->message_loop()->DeleteSoon(FROM_HERE,
748                                        notification_service_.release());
749  }
750}
751
752bool PasswordStoreMac::Init() {
753  thread_.reset(new base::Thread("Chrome_PasswordStore_Thread"));
754
755  if (!thread_->Start()) {
756    thread_.reset(NULL);
757    return false;
758  }
759  ScheduleTask(base::Bind(&PasswordStoreMac::CreateNotificationService, this));
760  return PasswordStore::Init();
761}
762
763void PasswordStoreMac::ShutdownOnUIThread() {
764}
765
766// Mac stores passwords in the system keychain, which can block for an
767// arbitrarily long time (most notably, it can block on user confirmation
768// from a dialog). Use a dedicated thread to avoid blocking DB thread.
769bool PasswordStoreMac::ScheduleTask(const base::Closure& task) {
770  if (thread_.get()) {
771    thread_->message_loop()->PostTask(FROM_HERE, task);
772    return true;
773  }
774  return false;
775}
776
777void PasswordStoreMac::ReportMetricsImpl() {
778  login_metadata_db_->ReportMetrics();
779}
780
781void PasswordStoreMac::AddLoginImpl(const PasswordForm& form) {
782  if (AddToKeychainIfNecessary(form)) {
783    if (login_metadata_db_->AddLogin(form)) {
784      PasswordStoreChangeList changes;
785      changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
786      content::NotificationService::current()->Notify(
787          chrome::NOTIFICATION_LOGINS_CHANGED,
788          content::Source<PasswordStore>(this),
789          content::Details<PasswordStoreChangeList>(&changes));
790    }
791  }
792}
793
794void PasswordStoreMac::UpdateLoginImpl(const PasswordForm& form) {
795  int update_count = 0;
796  if (!login_metadata_db_->UpdateLogin(form, &update_count))
797    return;
798
799  MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
800  if (update_count == 0 &&
801      !keychain_adapter.HasPasswordsMergeableWithForm(form)) {
802    // If the password isn't in either the DB or the keychain, then it must have
803    // been deleted after autofill happened, and should not be re-added.
804    return;
805  }
806
807  // The keychain add will update if there is a collision and add if there
808  // isn't, which is the behavior we want, so there's no separate update call.
809  if (AddToKeychainIfNecessary(form)) {
810    PasswordStoreChangeList changes;
811    if (update_count == 0) {
812      if (login_metadata_db_->AddLogin(form)) {
813        changes.push_back(PasswordStoreChange(PasswordStoreChange::ADD,
814                                              form));
815      }
816    } else {
817      changes.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE,
818                                            form));
819    }
820    if (!changes.empty()) {
821      content::NotificationService::current()->Notify(
822          chrome::NOTIFICATION_LOGINS_CHANGED,
823          content::Source<PasswordStore>(this),
824          content::Details<PasswordStoreChangeList>(&changes));
825    }
826  }
827}
828
829void PasswordStoreMac::RemoveLoginImpl(const PasswordForm& form) {
830  if (login_metadata_db_->RemoveLogin(form)) {
831    // See if we own a Keychain item associated with this item. We can do an
832    // exact search rather than messing around with trying to do fuzzy matching
833    // because passwords that we created will always have an exact-match
834    // database entry.
835    // (If a user does lose their profile but not their keychain we'll treat the
836    // entries we find like other imported entries anyway, so it's reasonable to
837    // handle deletes on them the way we would for an imported item.)
838    MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
839    owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
840    PasswordForm* owned_password_form =
841        owned_keychain_adapter.PasswordExactlyMatchingForm(form);
842    if (owned_password_form) {
843      // If we don't have other forms using it (i.e., a form differing only by
844      // the names of the form elements), delete the keychain entry.
845      if (!DatabaseHasFormMatchingKeychainForm(form)) {
846        owned_keychain_adapter.RemovePassword(form);
847      }
848    }
849
850    PasswordStoreChangeList changes;
851    changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form));
852    content::NotificationService::current()->Notify(
853        chrome::NOTIFICATION_LOGINS_CHANGED,
854        content::Source<PasswordStore>(this),
855        content::Details<PasswordStoreChangeList>(&changes));
856  }
857}
858
859void PasswordStoreMac::RemoveLoginsCreatedBetweenImpl(
860    const base::Time& delete_begin, const base::Time& delete_end) {
861  std::vector<PasswordForm*> forms;
862  if (login_metadata_db_->GetLoginsCreatedBetween(delete_begin, delete_end,
863                                                  &forms)) {
864    if (login_metadata_db_->RemoveLoginsCreatedBetween(delete_begin,
865                                                       delete_end)) {
866      // We can't delete from the Keychain by date because we may be sharing
867      // items with database entries that weren't in the delete range. Instead,
868      // we find all the Keychain items we own but aren't using any more and
869      // delete those.
870      std::vector<PasswordForm*> orphan_keychain_forms =
871          GetUnusedKeychainForms();
872      // This is inefficient, since we have to re-look-up each keychain item
873      // one at a time to delete it even though the search step already had a
874      // list of Keychain item references. If this turns out to be noticeably
875      // slow we'll need to rearchitect to allow the search and deletion steps
876      // to share.
877      RemoveKeychainForms(orphan_keychain_forms);
878      STLDeleteElements(&orphan_keychain_forms);
879
880      PasswordStoreChangeList changes;
881      for (std::vector<PasswordForm*>::const_iterator it = forms.begin();
882           it != forms.end(); ++it) {
883        changes.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE,
884                                              **it));
885      }
886      content::NotificationService::current()->Notify(
887          chrome::NOTIFICATION_LOGINS_CHANGED,
888          content::Source<PasswordStore>(this),
889          content::Details<PasswordStoreChangeList>(&changes));
890    }
891  }
892}
893
894void PasswordStoreMac::GetLoginsImpl(
895    const content::PasswordForm& form,
896    const ConsumerCallbackRunner& callback_runner) {
897  MacKeychainPasswordFormAdapter keychain_adapter(keychain_.get());
898  std::vector<PasswordForm*> keychain_forms =
899      keychain_adapter.PasswordsFillingForm(form);
900
901  std::vector<PasswordForm*> database_forms;
902  login_metadata_db_->GetLogins(form, &database_forms);
903
904  std::vector<PasswordForm*> matched_forms;
905  internal_keychain_helpers::MergePasswordForms(&keychain_forms,
906                                                &database_forms,
907                                                &matched_forms);
908
909  // Strip any blacklist entries out of the unused Keychain array, then take
910  // all the entries that are left (which we can use as imported passwords).
911  std::vector<PasswordForm*> keychain_blacklist_forms =
912      internal_keychain_helpers::ExtractBlacklistForms(&keychain_forms);
913  matched_forms.insert(matched_forms.end(),
914                       keychain_forms.begin(),
915                       keychain_forms.end());
916  keychain_forms.clear();
917  STLDeleteElements(&keychain_blacklist_forms);
918
919  // Clean up any orphaned database entries.
920  RemoveDatabaseForms(database_forms);
921  STLDeleteElements(&database_forms);
922
923  callback_runner.Run(matched_forms);
924}
925
926void PasswordStoreMac::GetBlacklistLoginsImpl(GetLoginsRequest* request) {
927  FillBlacklistLogins(&request->value);
928  ForwardLoginsResult(request);
929}
930
931void PasswordStoreMac::GetAutofillableLoginsImpl(GetLoginsRequest* request) {
932  FillAutofillableLogins(&request->value);
933  ForwardLoginsResult(request);
934}
935
936bool PasswordStoreMac::FillAutofillableLogins(
937         std::vector<PasswordForm*>* forms) {
938  DCHECK(thread_->message_loop() == base::MessageLoop::current());
939
940  std::vector<PasswordForm*> database_forms;
941  login_metadata_db_->GetAutofillableLogins(&database_forms);
942
943  std::vector<PasswordForm*> merged_forms =
944      internal_keychain_helpers::GetPasswordsForForms(*keychain_,
945                                                      &database_forms);
946
947  // Clean up any orphaned database entries.
948  RemoveDatabaseForms(database_forms);
949  STLDeleteElements(&database_forms);
950
951  forms->insert(forms->end(), merged_forms.begin(), merged_forms.end());
952  return true;
953}
954
955bool PasswordStoreMac::FillBlacklistLogins(
956         std::vector<PasswordForm*>* forms) {
957  DCHECK(thread_->message_loop() == base::MessageLoop::current());
958  return login_metadata_db_->GetBlacklistLogins(forms);
959}
960
961bool PasswordStoreMac::AddToKeychainIfNecessary(const PasswordForm& form) {
962  if (form.blacklisted_by_user) {
963    return true;
964  }
965  MacKeychainPasswordFormAdapter keychainAdapter(keychain_.get());
966  return keychainAdapter.AddPassword(form);
967}
968
969bool PasswordStoreMac::DatabaseHasFormMatchingKeychainForm(
970    const content::PasswordForm& form) {
971  bool has_match = false;
972  std::vector<PasswordForm*> database_forms;
973  login_metadata_db_->GetLogins(form, &database_forms);
974  for (std::vector<PasswordForm*>::iterator i = database_forms.begin();
975       i != database_forms.end(); ++i) {
976    if (internal_keychain_helpers::FormsMatchForMerge(form, **i) &&
977        (*i)->origin == form.origin) {
978      has_match = true;
979      break;
980    }
981  }
982  STLDeleteElements(&database_forms);
983  return has_match;
984}
985
986std::vector<PasswordForm*> PasswordStoreMac::GetUnusedKeychainForms() {
987  std::vector<PasswordForm*> database_forms;
988  login_metadata_db_->GetAutofillableLogins(&database_forms);
989
990  MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
991  owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
992  std::vector<PasswordForm*> owned_keychain_forms =
993      owned_keychain_adapter.GetAllPasswordFormPasswords();
994
995  // Run a merge; anything left in owned_keychain_forms when we are done no
996  // longer has a matching database entry.
997  std::vector<PasswordForm*> merged_forms;
998  internal_keychain_helpers::MergePasswordForms(&owned_keychain_forms,
999                                                &database_forms,
1000                                                &merged_forms);
1001  STLDeleteElements(&merged_forms);
1002  STLDeleteElements(&database_forms);
1003
1004  return owned_keychain_forms;
1005}
1006
1007void PasswordStoreMac::RemoveDatabaseForms(
1008    const std::vector<PasswordForm*>& forms) {
1009  for (std::vector<PasswordForm*>::const_iterator i = forms.begin();
1010       i != forms.end(); ++i) {
1011    login_metadata_db_->RemoveLogin(**i);
1012  }
1013}
1014
1015void PasswordStoreMac::RemoveKeychainForms(
1016    const std::vector<PasswordForm*>& forms) {
1017  MacKeychainPasswordFormAdapter owned_keychain_adapter(keychain_.get());
1018  owned_keychain_adapter.SetFindsOnlyOwnedItems(true);
1019  for (std::vector<PasswordForm*>::const_iterator i = forms.begin();
1020       i != forms.end(); ++i) {
1021    owned_keychain_adapter.RemovePassword(**i);
1022  }
1023}
1024
1025void PasswordStoreMac::CreateNotificationService() {
1026  notification_service_.reset(content::NotificationService::Create());
1027}
1028