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