Home | History | Annotate | Download | only in browser
      1 // Copyright (c) 2009 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 "base/logging.h"
      6 #include "base/time.h"
      7 #include "chrome/browser/keychain_mock_mac.h"
      8 
      9 MockKeychain::MockKeychain(unsigned int item_capacity)
     10     : item_capacity_(item_capacity), item_count_(0), search_copy_count_(0),
     11       keychain_item_copy_count_(0), attribute_data_copy_count_(0),
     12       find_generic_result_(noErr), called_add_generic_(false),
     13       password_data_count_(0) {
     14   UInt32 tags[] = { kSecAccountItemAttr,
     15                     kSecServerItemAttr,
     16                     kSecPortItemAttr,
     17                     kSecPathItemAttr,
     18                     kSecProtocolItemAttr,
     19                     kSecAuthenticationTypeItemAttr,
     20                     kSecSecurityDomainItemAttr,
     21                     kSecCreationDateItemAttr,
     22                     kSecNegativeItemAttr,
     23                     kSecCreatorItemAttr };
     24 
     25   // Create the test keychain data storage.
     26   keychain_attr_list_ = static_cast<SecKeychainAttributeList*>(
     27       calloc(item_capacity_, sizeof(SecKeychainAttributeList)));
     28   keychain_data_ = static_cast<KeychainPasswordData*>(
     29       calloc(item_capacity_, sizeof(KeychainPasswordData)));
     30   for (unsigned int i = 0; i < item_capacity_; ++i) {
     31     keychain_attr_list_[i].count = arraysize(tags);
     32     keychain_attr_list_[i].attr = static_cast<SecKeychainAttribute*>(
     33         calloc(keychain_attr_list_[i].count, sizeof(SecKeychainAttribute)));
     34     for (unsigned int j = 0; j < keychain_attr_list_[i].count; ++j) {
     35       keychain_attr_list_[i].attr[j].tag = tags[j];
     36       size_t data_size = 0;
     37       switch (tags[j]) {
     38         case kSecPortItemAttr:
     39           data_size = sizeof(UInt32);
     40           break;
     41         case kSecProtocolItemAttr:
     42           data_size = sizeof(SecProtocolType);
     43           break;
     44         case kSecAuthenticationTypeItemAttr:
     45           data_size = sizeof(SecAuthenticationType);
     46           break;
     47         case kSecNegativeItemAttr:
     48           data_size = sizeof(Boolean);
     49           break;
     50         case kSecCreatorItemAttr:
     51           data_size = sizeof(OSType);
     52           break;
     53       }
     54       if (data_size > 0) {
     55         keychain_attr_list_[i].attr[j].length = data_size;
     56         keychain_attr_list_[i].attr[j].data = calloc(1, data_size);
     57       }
     58     }
     59   }
     60 }
     61 
     62 MockKeychain::~MockKeychain() {
     63   for (unsigned int i = 0; i < item_capacity_; ++i) {
     64     for (unsigned int j = 0; j < keychain_attr_list_[i].count; ++j) {
     65       if (keychain_attr_list_[i].attr[j].data) {
     66         free(keychain_attr_list_[i].attr[j].data);
     67       }
     68     }
     69     free(keychain_attr_list_[i].attr);
     70     if (keychain_data_[i].data) {
     71       free(keychain_data_[i].data);
     72     }
     73   }
     74   free(keychain_attr_list_);
     75   free(keychain_data_);
     76 }
     77 
     78 
     79 SecKeychainAttribute* MockKeychain::AttributeWithTag(
     80     const SecKeychainAttributeList& attribute_list, UInt32 tag) {
     81   int attribute_index = -1;
     82   for (unsigned int i = 0; i < attribute_list.count; ++i) {
     83     if (attribute_list.attr[i].tag == tag) {
     84       attribute_index = i;
     85       break;
     86     }
     87   }
     88   if (attribute_index == -1) {
     89     NOTREACHED() << "Unsupported attribute: " << tag;
     90     return NULL;
     91   }
     92   return &(attribute_list.attr[attribute_index]);
     93 }
     94 
     95 void MockKeychain::SetTestDataBytes(int item, UInt32 tag, const void* data,
     96                                     size_t length) {
     97   SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
     98                                                      tag);
     99   attribute->length = length;
    100   if (length > 0) {
    101     if (attribute->data) {
    102       free(attribute->data);
    103     }
    104     attribute->data = malloc(length);
    105     CHECK(attribute->data);
    106     memcpy(attribute->data, data, length);
    107   } else {
    108     attribute->data = NULL;
    109   }
    110 }
    111 
    112 void MockKeychain::SetTestDataString(int item, UInt32 tag, const char* value) {
    113   SetTestDataBytes(item, tag, value, value ? strlen(value) : 0);
    114 }
    115 
    116 void MockKeychain::SetTestDataPort(int item, UInt32 value) {
    117   SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
    118                                                      kSecPortItemAttr);
    119   UInt32* data = static_cast<UInt32*>(attribute->data);
    120   *data = value;
    121 }
    122 
    123 void MockKeychain::SetTestDataProtocol(int item, SecProtocolType value) {
    124   SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
    125                                                      kSecProtocolItemAttr);
    126   SecProtocolType* data = static_cast<SecProtocolType*>(attribute->data);
    127   *data = value;
    128 }
    129 
    130 void MockKeychain::SetTestDataAuthType(int item, SecAuthenticationType value) {
    131   SecKeychainAttribute* attribute = AttributeWithTag(
    132       keychain_attr_list_[item], kSecAuthenticationTypeItemAttr);
    133   SecAuthenticationType* data = static_cast<SecAuthenticationType*>(
    134       attribute->data);
    135   *data = value;
    136 }
    137 
    138 void MockKeychain::SetTestDataNegativeItem(int item, Boolean value) {
    139   SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
    140                                                      kSecNegativeItemAttr);
    141   Boolean* data = static_cast<Boolean*>(attribute->data);
    142   *data = value;
    143 }
    144 
    145 void MockKeychain::SetTestDataCreator(int item, OSType value) {
    146   SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
    147                                                      kSecCreatorItemAttr);
    148   OSType* data = static_cast<OSType*>(attribute->data);
    149   *data = value;
    150 }
    151 
    152 void MockKeychain::SetTestDataPasswordBytes(int item, const void* data,
    153                                             size_t length) {
    154   keychain_data_[item].length = length;
    155   if (length > 0) {
    156     if (keychain_data_[item].data) {
    157       free(keychain_data_[item].data);
    158     }
    159     keychain_data_[item].data = malloc(length);
    160     memcpy(keychain_data_[item].data, data, length);
    161   } else {
    162     keychain_data_[item].data = NULL;
    163   }
    164 }
    165 
    166 void MockKeychain::SetTestDataPasswordString(int item, const char* value) {
    167   SetTestDataPasswordBytes(item, value, value ? strlen(value) : 0);
    168 }
    169 
    170 OSStatus MockKeychain::ItemCopyAttributesAndData(
    171     SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info,
    172     SecItemClass *itemClass, SecKeychainAttributeList **attrList,
    173     UInt32 *length, void **outData) const {
    174   DCHECK(itemRef);
    175   unsigned int item_index = reinterpret_cast<unsigned int>(itemRef) - 1;
    176   if (item_index >= item_count_) {
    177     return errSecInvalidItemRef;
    178   }
    179 
    180   DCHECK(!itemClass);  // itemClass not implemented in the Mock.
    181   if (attrList) {
    182     *attrList  = &(keychain_attr_list_[item_index]);
    183   }
    184   if (outData) {
    185     *outData = keychain_data_[item_index].data;
    186     DCHECK(length);
    187     *length = keychain_data_[item_index].length;
    188   }
    189 
    190   ++attribute_data_copy_count_;
    191   return noErr;
    192 }
    193 
    194 OSStatus MockKeychain::ItemModifyAttributesAndData(
    195     SecKeychainItemRef itemRef, const SecKeychainAttributeList *attrList,
    196     UInt32 length, const void *data) const {
    197   DCHECK(itemRef);
    198   const char* fail_trigger = "fail_me";
    199   if (length == strlen(fail_trigger) &&
    200       memcmp(data, fail_trigger, length) == 0) {
    201     return errSecAuthFailed;
    202   }
    203 
    204   unsigned int item_index = reinterpret_cast<unsigned int>(itemRef) - 1;
    205   if (item_index >= item_count_) {
    206     return errSecInvalidItemRef;
    207   }
    208 
    209   MockKeychain* mutable_this = const_cast<MockKeychain*>(this);
    210   if (attrList) {
    211     for (UInt32 change_attr = 0; change_attr < attrList->count; ++change_attr) {
    212       if (attrList->attr[change_attr].tag == kSecCreatorItemAttr) {
    213         void* data = attrList->attr[change_attr].data;
    214         mutable_this->SetTestDataCreator(item_index,
    215                                          *(static_cast<OSType*>(data)));
    216       } else {
    217         NOTIMPLEMENTED();
    218       }
    219     }
    220   }
    221   if (data) {
    222     mutable_this->SetTestDataPasswordBytes(item_index, data, length);
    223   }
    224   return noErr;
    225 }
    226 
    227 OSStatus MockKeychain::ItemFreeAttributesAndData(
    228     SecKeychainAttributeList *attrList,
    229     void *data) const {
    230   --attribute_data_copy_count_;
    231   return noErr;
    232 }
    233 
    234 OSStatus MockKeychain::ItemDelete(SecKeychainItemRef itemRef) const {
    235   unsigned int item_index = reinterpret_cast<unsigned int>(itemRef) - 1;
    236   // The mock only supports deleting the last item.
    237   if (item_index != item_count_ - 1) {
    238     NOTIMPLEMENTED();
    239   }
    240   --item_count_;
    241   return noErr;
    242 }
    243 
    244 OSStatus MockKeychain::SearchCreateFromAttributes(
    245     CFTypeRef keychainOrArray, SecItemClass itemClass,
    246     const SecKeychainAttributeList *attrList,
    247     SecKeychainSearchRef *searchRef) const {
    248   // Figure out which of our mock items matches, and set up the array we'll use
    249   // to generate results out of SearchCopyNext.
    250   remaining_search_results_.clear();
    251   for (unsigned int mock_item = 0; mock_item < item_count_; ++mock_item) {
    252     bool mock_item_matches = true;
    253     for (UInt32 search_attr = 0; search_attr < attrList->count; ++search_attr) {
    254       SecKeychainAttribute* mock_attribute =
    255           AttributeWithTag(keychain_attr_list_[mock_item],
    256                            attrList->attr[search_attr].tag);
    257       if (mock_attribute->length != attrList->attr[search_attr].length ||
    258           memcmp(mock_attribute->data, attrList->attr[search_attr].data,
    259                  attrList->attr[search_attr].length) != 0) {
    260         mock_item_matches = false;
    261         break;
    262       }
    263     }
    264     if (mock_item_matches) {
    265       remaining_search_results_.push_back(mock_item);
    266     }
    267   }
    268 
    269   DCHECK(searchRef);
    270   *searchRef = reinterpret_cast<SecKeychainSearchRef>(kDummySearchRef);
    271   ++search_copy_count_;
    272   return noErr;
    273 }
    274 
    275 OSStatus MockKeychain::AddInternetPassword(
    276     SecKeychainRef keychain,
    277     UInt32 serverNameLength, const char *serverName,
    278     UInt32 securityDomainLength, const char *securityDomain,
    279     UInt32 accountNameLength, const char *accountName,
    280     UInt32 pathLength, const char *path,
    281     UInt16 port, SecProtocolType protocol,
    282     SecAuthenticationType authenticationType,
    283     UInt32 passwordLength, const void *passwordData,
    284     SecKeychainItemRef *itemRef) const {
    285 
    286   // Check for the magic duplicate item trigger.
    287   if (strcmp(serverName, "some.domain.com") == 0) {
    288     return errSecDuplicateItem;
    289   }
    290 
    291   // Use empty slots until they run out, then just keep replacing the last item.
    292   int target_item = (item_count_ == item_capacity_) ? item_capacity_ - 1
    293                                                     : item_count_++;
    294 
    295   MockKeychain* mutable_this = const_cast<MockKeychain*>(this);
    296   mutable_this->SetTestDataBytes(target_item, kSecServerItemAttr, serverName,
    297                                  serverNameLength);
    298   mutable_this->SetTestDataBytes(target_item, kSecSecurityDomainItemAttr,
    299                                  securityDomain, securityDomainLength);
    300   mutable_this->SetTestDataBytes(target_item, kSecAccountItemAttr, accountName,
    301                                  accountNameLength);
    302   mutable_this->SetTestDataBytes(target_item, kSecPathItemAttr, path,
    303                                  pathLength);
    304   mutable_this->SetTestDataPort(target_item, port);
    305   mutable_this->SetTestDataProtocol(target_item, protocol);
    306   mutable_this->SetTestDataAuthType(target_item, authenticationType);
    307   mutable_this->SetTestDataPasswordBytes(target_item, passwordData,
    308                                          passwordLength);
    309   base::Time::Exploded exploded_time;
    310   base::Time::Now().UTCExplode(&exploded_time);
    311   char time_string[128];
    312   snprintf(time_string, sizeof(time_string), "%04d%02d%02d%02d%02d%02dZ",
    313            exploded_time.year, exploded_time.month, exploded_time.day_of_month,
    314            exploded_time.hour, exploded_time.minute, exploded_time.second);
    315   mutable_this->SetTestDataString(target_item, kSecCreationDateItemAttr,
    316                                   time_string);
    317 
    318   added_via_api_.insert(target_item);
    319 
    320   if (itemRef) {
    321     *itemRef = reinterpret_cast<SecKeychainItemRef>(target_item + 1);
    322     ++keychain_item_copy_count_;
    323   }
    324   return noErr;
    325 }
    326 
    327 OSStatus MockKeychain::SearchCopyNext(SecKeychainSearchRef searchRef,
    328                                       SecKeychainItemRef *itemRef) const {
    329   if (remaining_search_results_.empty()) {
    330     return errSecItemNotFound;
    331   }
    332   unsigned int index = remaining_search_results_.front();
    333   remaining_search_results_.erase(remaining_search_results_.begin());
    334   *itemRef = reinterpret_cast<SecKeychainItemRef>(index + 1);
    335   ++keychain_item_copy_count_;
    336   return noErr;
    337 }
    338 
    339 OSStatus MockKeychain::FindGenericPassword(CFTypeRef keychainOrArray,
    340                                            UInt32 serviceNameLength,
    341                                            const char *serviceName,
    342                                            UInt32 accountNameLength,
    343                                            const char *accountName,
    344                                            UInt32 *passwordLength,
    345                                            void **passwordData,
    346                                            SecKeychainItemRef *itemRef) const {
    347   // When simulating |noErr| we return canned |passwordData| and
    348   // |passwordLenght|.  Otherwise, just return given code.
    349   if (find_generic_result_ == noErr) {
    350     static char password[] = "my_password";
    351 
    352     DCHECK(passwordData);
    353     *passwordData = static_cast<void*>(password);
    354     DCHECK(passwordLength);
    355     *passwordLength = strlen(password);
    356     password_data_count_++;
    357   }
    358 
    359   return find_generic_result_;
    360 }
    361 
    362 OSStatus MockKeychain::ItemFreeContent(SecKeychainAttributeList *attrList,
    363                                        void *data) const {
    364   // No-op.
    365   password_data_count_--;
    366   return noErr;
    367 }
    368 
    369 OSStatus MockKeychain::AddGenericPassword(SecKeychainRef keychain,
    370                                           UInt32 serviceNameLength,
    371                                           const char *serviceName,
    372                                           UInt32 accountNameLength,
    373                                           const char *accountName,
    374                                           UInt32 passwordLength,
    375                                           const void *passwordData,
    376                                           SecKeychainItemRef *itemRef) const {
    377   called_add_generic_ = true;
    378 
    379   DCHECK(passwordLength > 0);
    380   DCHECK(passwordData);
    381   add_generic_password_ =
    382       std::string(const_cast<char*>(static_cast<const char*>(passwordData)),
    383                   passwordLength);
    384   return noErr;
    385 }
    386 
    387 void MockKeychain::Free(CFTypeRef ref) const {
    388   if (!ref) {
    389     return;
    390   }
    391 
    392   if (reinterpret_cast<int>(ref) == kDummySearchRef) {
    393     --search_copy_count_;
    394   } else {
    395     --keychain_item_copy_count_;
    396   }
    397 }
    398 
    399 int MockKeychain::UnfreedSearchCount() const {
    400   return search_copy_count_;
    401 }
    402 
    403 int MockKeychain::UnfreedKeychainItemCount() const {
    404   return keychain_item_copy_count_;
    405 }
    406 
    407 int MockKeychain::UnfreedAttributeDataCount() const {
    408   return attribute_data_copy_count_;
    409 }
    410 
    411 bool MockKeychain::CreatorCodesSetForAddedItems() const {
    412   for (std::set<unsigned int>::const_iterator i = added_via_api_.begin();
    413        i != added_via_api_.end(); ++i) {
    414     SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[*i],
    415                                                        kSecCreatorItemAttr);
    416     OSType* data = static_cast<OSType*>(attribute->data);
    417     if (*data == 0) {
    418       return false;
    419     }
    420   }
    421   return true;
    422 }
    423 
    424 void MockKeychain::AddTestItem(const KeychainTestData& item_data) {
    425   unsigned int index = item_count_++;
    426   CHECK(index < item_capacity_);
    427 
    428   SetTestDataAuthType(index, item_data.auth_type);
    429   SetTestDataString(index, kSecServerItemAttr, item_data.server);
    430   SetTestDataProtocol(index, item_data.protocol);
    431   SetTestDataString(index, kSecPathItemAttr, item_data.path);
    432   SetTestDataPort(index, item_data.port);
    433   SetTestDataString(index, kSecSecurityDomainItemAttr,
    434                     item_data.security_domain);
    435   SetTestDataString(index, kSecCreationDateItemAttr, item_data.creation_date);
    436   SetTestDataString(index, kSecAccountItemAttr, item_data.username);
    437   SetTestDataPasswordString(index, item_data.password);
    438   SetTestDataNegativeItem(index, item_data.negative_item);
    439 }
    440