Home | History | Annotate | Download | only in password_manager
      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/message_loop.h"
     18 #include "base/stl_util.h"
     19 #include "base/strings/string_util.h"
     20 #include "base/strings/utf_string_conversions.h"
     21 #include "chrome/browser/chrome_notification_types.h"
     22 #include "chrome/browser/password_manager/login_database.h"
     23 #include "chrome/browser/password_manager/password_store_change.h"
     24 #include "content/public/browser/notification_service.h"
     25 #include "crypto/apple_keychain.h"
     26 
     27 using crypto::AppleKeychain;
     28 using content::PasswordForm;
     29 
     30 // Utility class to handle the details of constructing and running a keychain
     31 // search from a set of attributes.
     32 class 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 
     57 KeychainSearch::KeychainSearch(const AppleKeychain& keychain)
     58     : keychain_(&keychain), search_ref_(NULL) {
     59   search_attributes_.count = 0;
     60   search_attributes_.attr = NULL;
     61 }
     62 
     63 KeychainSearch::~KeychainSearch() {
     64   if (search_attributes_.attr) {
     65     free(search_attributes_.attr);
     66   }
     67 }
     68 
     69 void 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 
    150 void 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.
    174 namespace 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.
    178 GURL 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.
    200 bool 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|.
    225 PasswordForm::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 
    234 bool 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 
    347 bool 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.
    360 PasswordForm* 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|.
    382 std::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.
    399 template <class T>
    400 void 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 
    412 void 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 
    446 std::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 
    472 MacKeychainPasswordFormAdapter::MacKeychainPasswordFormAdapter(
    473     const AppleKeychain* keychain)
    474     : keychain_(keychain), finds_only_owned_(false) {
    475 }
    476 
    477 std::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 
    487 std::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 
    498 PasswordForm* 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 
    512 bool 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 
    526 std::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 
    545 bool 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 
    589 bool 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 
    598 void MacKeychainPasswordFormAdapter::SetFindsOnlyOwnedItems(
    599     bool finds_only_owned) {
    600   finds_only_owned_ = finds_only_owned;
    601 }
    602 
    603 std::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 
    620 SecKeychainItemRef 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 
    644 std::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.
    675 bool 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|.
    702 SecAuthenticationType 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 
    714 bool 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 
    722 bool 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 
    732 OSType MacKeychainPasswordFormAdapter::CreatorCodeForSearch() {
    733   return finds_only_owned_ ? base::mac::CreatorCodeForApplication() : 0;
    734 }
    735 
    736 #pragma mark -
    737 
    738 PasswordStoreMac::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 
    745 PasswordStoreMac::~PasswordStoreMac() {
    746   if (thread_.get()) {
    747     thread_->message_loop()->DeleteSoon(FROM_HERE,
    748                                         notification_service_.release());
    749   }
    750 }
    751 
    752 bool 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 
    763 void 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.
    769 bool 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 
    777 void PasswordStoreMac::ReportMetricsImpl() {
    778   login_metadata_db_->ReportMetrics();
    779 }
    780 
    781 void 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 
    794 void 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 
    829 void 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 
    859 void 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 
    894 void 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 
    926 void PasswordStoreMac::GetBlacklistLoginsImpl(GetLoginsRequest* request) {
    927   FillBlacklistLogins(&request->value);
    928   ForwardLoginsResult(request);
    929 }
    930 
    931 void PasswordStoreMac::GetAutofillableLoginsImpl(GetLoginsRequest* request) {
    932   FillAutofillableLogins(&request->value);
    933   ForwardLoginsResult(request);
    934 }
    935 
    936 bool 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 
    955 bool PasswordStoreMac::FillBlacklistLogins(
    956          std::vector<PasswordForm*>* forms) {
    957   DCHECK(thread_->message_loop() == base::MessageLoop::current());
    958   return login_metadata_db_->GetBlacklistLogins(forms);
    959 }
    960 
    961 bool 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 
    969 bool 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 
    986 std::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 
   1007 void 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 
   1015 void 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 
   1025 void PasswordStoreMac::CreateNotificationService() {
   1026   notification_service_.reset(content::NotificationService::Create());
   1027 }
   1028