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