root/crypto/mock_apple_keychain_mac.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. password_data_count_
  2. InitializeKeychainData
  3. AttributeWithTag
  4. SetTestDataBytes
  5. SetTestDataString
  6. SetTestDataPort
  7. SetTestDataProtocol
  8. SetTestDataAuthType
  9. SetTestDataNegativeItem
  10. SetTestDataCreator
  11. SetTestDataPasswordBytes
  12. SetTestDataPasswordString
  13. ItemCopyAttributesAndData
  14. ItemModifyAttributesAndData
  15. ItemFreeAttributesAndData
  16. ItemDelete
  17. SearchCreateFromAttributes
  18. AlreadyContainsInternetPassword
  19. AddInternetPassword
  20. SearchCopyNext
  21. Free
  22. UnfreedSearchCount
  23. UnfreedKeychainItemCount
  24. UnfreedAttributeDataCount
  25. CreatorCodesSetForAddedItems
  26. AddTestItem

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/logging.h"
#include "base/time/time.h"
#include "crypto/mock_apple_keychain.h"

namespace crypto {

// static
const SecKeychainSearchRef MockAppleKeychain::kDummySearchRef =
    reinterpret_cast<SecKeychainSearchRef>(1000);

MockAppleKeychain::MockAppleKeychain()
    : next_item_key_(0),
      search_copy_count_(0),
      keychain_item_copy_count_(0),
      attribute_data_copy_count_(0),
      find_generic_result_(noErr),
      called_add_generic_(false),
      password_data_count_(0) {}

void MockAppleKeychain::InitializeKeychainData(MockKeychainItemType key) const {
  UInt32 tags[] = { kSecAccountItemAttr,
                    kSecServerItemAttr,
                    kSecPortItemAttr,
                    kSecPathItemAttr,
                    kSecProtocolItemAttr,
                    kSecAuthenticationTypeItemAttr,
                    kSecSecurityDomainItemAttr,
                    kSecCreationDateItemAttr,
                    kSecNegativeItemAttr,
                    kSecCreatorItemAttr };
  keychain_attr_list_[key] = SecKeychainAttributeList();
  keychain_data_[key] = KeychainPasswordData();
  keychain_attr_list_[key].count = arraysize(tags);
  keychain_attr_list_[key].attr = static_cast<SecKeychainAttribute*>(
      calloc(keychain_attr_list_[key].count, sizeof(SecKeychainAttribute)));
  for (unsigned int i = 0; i < keychain_attr_list_[key].count; ++i) {
    keychain_attr_list_[key].attr[i].tag = tags[i];
    size_t data_size = 0;
    switch (tags[i]) {
      case kSecPortItemAttr:
        data_size = sizeof(UInt32);
        break;
      case kSecProtocolItemAttr:
        data_size = sizeof(SecProtocolType);
        break;
      case kSecAuthenticationTypeItemAttr:
        data_size = sizeof(SecAuthenticationType);
        break;
      case kSecNegativeItemAttr:
        data_size = sizeof(Boolean);
        break;
      case kSecCreatorItemAttr:
        data_size = sizeof(OSType);
        break;
    }
    if (data_size > 0) {
      keychain_attr_list_[key].attr[i].length = data_size;
      keychain_attr_list_[key].attr[i].data = calloc(1, data_size);
    }
  }
}

MockAppleKeychain::~MockAppleKeychain() {
  for (MockKeychainAttributesMap::iterator it = keychain_attr_list_.begin();
       it != keychain_attr_list_.end();
       ++it) {
    for (unsigned int i = 0; i < it->second.count; ++i) {
      if (it->second.attr[i].data)
        free(it->second.attr[i].data);
    }
    free(it->second.attr);
    if (keychain_data_[it->first].data)
      free(keychain_data_[it->first].data);
  }
  keychain_attr_list_.clear();
  keychain_data_.clear();
}

SecKeychainAttribute* MockAppleKeychain::AttributeWithTag(
    const SecKeychainAttributeList& attribute_list,
    UInt32 tag) {
  int attribute_index = -1;
  for (unsigned int i = 0; i < attribute_list.count; ++i) {
    if (attribute_list.attr[i].tag == tag) {
      attribute_index = i;
      break;
    }
  }
  if (attribute_index == -1) {
    NOTREACHED() << "Unsupported attribute: " << tag;
    return NULL;
  }
  return &(attribute_list.attr[attribute_index]);
}

void MockAppleKeychain::SetTestDataBytes(MockKeychainItemType item,
                                         UInt32 tag,
                                         const void* data,
                                         size_t length) {
  SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
                                                     tag);
  attribute->length = length;
  if (length > 0) {
    if (attribute->data)
      free(attribute->data);
    attribute->data = malloc(length);
    CHECK(attribute->data);
    memcpy(attribute->data, data, length);
  } else {
    attribute->data = NULL;
  }
}

void MockAppleKeychain::SetTestDataString(MockKeychainItemType item,
                                          UInt32 tag,
                                          const char* value) {
  SetTestDataBytes(item, tag, value, value ? strlen(value) : 0);
}

void MockAppleKeychain::SetTestDataPort(MockKeychainItemType item,
                                        UInt32 value) {
  SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
                                                     kSecPortItemAttr);
  UInt32* data = static_cast<UInt32*>(attribute->data);
  *data = value;
}

void MockAppleKeychain::SetTestDataProtocol(MockKeychainItemType item,
                                            SecProtocolType value) {
  SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
                                                     kSecProtocolItemAttr);
  SecProtocolType* data = static_cast<SecProtocolType*>(attribute->data);
  *data = value;
}

void MockAppleKeychain::SetTestDataAuthType(MockKeychainItemType item,
                                            SecAuthenticationType value) {
  SecKeychainAttribute* attribute = AttributeWithTag(
      keychain_attr_list_[item], kSecAuthenticationTypeItemAttr);
  SecAuthenticationType* data = static_cast<SecAuthenticationType*>(
      attribute->data);
  *data = value;
}

void MockAppleKeychain::SetTestDataNegativeItem(MockKeychainItemType item,
                                                Boolean value) {
  SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
                                                     kSecNegativeItemAttr);
  Boolean* data = static_cast<Boolean*>(attribute->data);
  *data = value;
}

void MockAppleKeychain::SetTestDataCreator(MockKeychainItemType item,
                                           OSType value) {
  SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
                                                     kSecCreatorItemAttr);
  OSType* data = static_cast<OSType*>(attribute->data);
  *data = value;
}

void MockAppleKeychain::SetTestDataPasswordBytes(MockKeychainItemType item,
                                                 const void* data,
                                                 size_t length) {
  keychain_data_[item].length = length;
  if (length > 0) {
    if (keychain_data_[item].data)
      free(keychain_data_[item].data);
    keychain_data_[item].data = malloc(length);
    memcpy(keychain_data_[item].data, data, length);
  } else {
    keychain_data_[item].data = NULL;
  }
}

void MockAppleKeychain::SetTestDataPasswordString(MockKeychainItemType item,
                                                  const char* value) {
  SetTestDataPasswordBytes(item, value, value ? strlen(value) : 0);
}

OSStatus MockAppleKeychain::ItemCopyAttributesAndData(
    SecKeychainItemRef itemRef,
    SecKeychainAttributeInfo* info,
    SecItemClass* itemClass,
    SecKeychainAttributeList** attrList,
    UInt32* length,
    void** outData) const {
  DCHECK(itemRef);
  MockKeychainItemType key =
      reinterpret_cast<MockKeychainItemType>(itemRef) - 1;
  if (keychain_attr_list_.find(key) == keychain_attr_list_.end())
    return errSecInvalidItemRef;

  DCHECK(!itemClass);  // itemClass not implemented in the Mock.
  if (attrList)
    *attrList  = &(keychain_attr_list_[key]);
  if (outData) {
    *outData = keychain_data_[key].data;
    DCHECK(length);
    *length = keychain_data_[key].length;
  }

  ++attribute_data_copy_count_;
  return noErr;
}

OSStatus MockAppleKeychain::ItemModifyAttributesAndData(
    SecKeychainItemRef itemRef,
    const SecKeychainAttributeList* attrList,
    UInt32 length,
    const void* data) const {
  DCHECK(itemRef);
  const char* fail_trigger = "fail_me";
  if (length == strlen(fail_trigger) &&
      memcmp(data, fail_trigger, length) == 0) {
    return errSecAuthFailed;
  }

  MockKeychainItemType key =
      reinterpret_cast<MockKeychainItemType>(itemRef) - 1;
  if (keychain_attr_list_.find(key) == keychain_attr_list_.end())
    return errSecInvalidItemRef;

  MockAppleKeychain* mutable_this = const_cast<MockAppleKeychain*>(this);
  if (attrList) {
    for (UInt32 change_attr = 0; change_attr < attrList->count; ++change_attr) {
      if (attrList->attr[change_attr].tag == kSecCreatorItemAttr) {
        void* data = attrList->attr[change_attr].data;
        mutable_this->SetTestDataCreator(key, *(static_cast<OSType*>(data)));
      } else {
        NOTIMPLEMENTED();
      }
    }
  }
  if (data)
    mutable_this->SetTestDataPasswordBytes(key, data, length);
  return noErr;
}

OSStatus MockAppleKeychain::ItemFreeAttributesAndData(
    SecKeychainAttributeList* attrList,
    void* data) const {
  --attribute_data_copy_count_;
  return noErr;
}

OSStatus MockAppleKeychain::ItemDelete(SecKeychainItemRef itemRef) const {
  MockKeychainItemType key =
      reinterpret_cast<MockKeychainItemType>(itemRef) - 1;

  for (unsigned int i = 0; i < keychain_attr_list_[key].count; ++i) {
    if (keychain_attr_list_[key].attr[i].data)
      free(keychain_attr_list_[key].attr[i].data);
  }
  free(keychain_attr_list_[key].attr);
  if (keychain_data_[key].data)
    free(keychain_data_[key].data);

  keychain_attr_list_.erase(key);
  keychain_data_.erase(key);
  added_via_api_.erase(key);
  return noErr;
}

OSStatus MockAppleKeychain::SearchCreateFromAttributes(
    CFTypeRef keychainOrArray,
    SecItemClass itemClass,
    const SecKeychainAttributeList* attrList,
    SecKeychainSearchRef* searchRef) const {
  // Figure out which of our mock items matches, and set up the array we'll use
  // to generate results out of SearchCopyNext.
  remaining_search_results_.clear();
  for (MockKeychainAttributesMap::const_iterator it =
           keychain_attr_list_.begin();
       it != keychain_attr_list_.end();
       ++it) {
    bool mock_item_matches = true;
    for (UInt32 search_attr = 0; search_attr < attrList->count; ++search_attr) {
      SecKeychainAttribute* mock_attribute =
          AttributeWithTag(it->second, attrList->attr[search_attr].tag);
      if (mock_attribute->length != attrList->attr[search_attr].length ||
          memcmp(mock_attribute->data, attrList->attr[search_attr].data,
                 attrList->attr[search_attr].length) != 0) {
        mock_item_matches = false;
        break;
      }
    }
    if (mock_item_matches)
      remaining_search_results_.push_back(it->first);
  }

  DCHECK(searchRef);
  *searchRef = kDummySearchRef;
  ++search_copy_count_;
  return noErr;
}

bool MockAppleKeychain::AlreadyContainsInternetPassword(
    UInt32 serverNameLength,
    const char* serverName,
    UInt32 securityDomainLength,
    const char* securityDomain,
    UInt32 accountNameLength,
    const char* accountName,
    UInt32 pathLength,
    const char* path,
    UInt16 port,
    SecProtocolType protocol,
    SecAuthenticationType authenticationType) const {
  for (MockKeychainAttributesMap::const_iterator it =
           keychain_attr_list_.begin();
       it != keychain_attr_list_.end();
       ++it) {
    SecKeychainAttribute* attribute;
    attribute = AttributeWithTag(it->second, kSecServerItemAttr);
    if ((attribute->length != serverNameLength) ||
        (attribute->data == NULL && *serverName != '\0') ||
        (attribute->data != NULL && *serverName == '\0') ||
        strncmp(serverName,
                (const char*) attribute->data,
                serverNameLength) != 0) {
      continue;
    }
    attribute = AttributeWithTag(it->second, kSecSecurityDomainItemAttr);
    if ((attribute->length != securityDomainLength) ||
        (attribute->data == NULL && *securityDomain != '\0') ||
        (attribute->data != NULL && *securityDomain == '\0') ||
        strncmp(securityDomain,
                (const char*) attribute->data,
                securityDomainLength) != 0) {
      continue;
    }
    attribute = AttributeWithTag(it->second, kSecAccountItemAttr);
    if ((attribute->length != accountNameLength) ||
        (attribute->data == NULL && *accountName != '\0') ||
        (attribute->data != NULL && *accountName == '\0') ||
        strncmp(accountName,
                (const char*) attribute->data,
                accountNameLength) != 0) {
      continue;
    }
    attribute = AttributeWithTag(it->second, kSecPathItemAttr);
    if ((attribute->length != pathLength) ||
        (attribute->data == NULL && *path != '\0') ||
        (attribute->data != NULL && *path == '\0') ||
        strncmp(path,
                (const char*) attribute->data,
                pathLength) != 0) {
      continue;
    }
    attribute = AttributeWithTag(it->second, kSecPortItemAttr);
    if ((attribute->data == NULL) ||
        (port != *(static_cast<UInt32*>(attribute->data)))) {
      continue;
    }
    attribute = AttributeWithTag(it->second, kSecProtocolItemAttr);
    if ((attribute->data == NULL) ||
        (protocol != *(static_cast<SecProtocolType*>(attribute->data)))) {
      continue;
    }
    attribute = AttributeWithTag(it->second, kSecAuthenticationTypeItemAttr);
    if ((attribute->data == NULL) ||
        (authenticationType !=
            *(static_cast<SecAuthenticationType*>(attribute->data)))) {
      continue;
    }
    // The keychain already has this item, since all fields other than the
    // password match.
    return true;
  }
  return false;
}

OSStatus MockAppleKeychain::AddInternetPassword(
    SecKeychainRef keychain,
    UInt32 serverNameLength,
    const char* serverName,
    UInt32 securityDomainLength,
    const char* securityDomain,
    UInt32 accountNameLength,
    const char* accountName,
    UInt32 pathLength,
    const char* path,
    UInt16 port,
    SecProtocolType protocol,
    SecAuthenticationType authenticationType,
    UInt32 passwordLength,
    const void* passwordData,
    SecKeychainItemRef* itemRef) const {

  // Check for the magic duplicate item trigger.
  if (strcmp(serverName, "some.domain.com") == 0)
    return errSecDuplicateItem;

  // If the account already exists in the keychain, we don't add it.
  if (AlreadyContainsInternetPassword(serverNameLength, serverName,
                                      securityDomainLength, securityDomain,
                                      accountNameLength, accountName,
                                      pathLength, path,
                                      port, protocol,
                                      authenticationType)) {
    return errSecDuplicateItem;
  }

  // Pick the next unused slot.
  MockKeychainItemType key = next_item_key_++;

  // Initialize keychain data storage at the target location.
  InitializeKeychainData(key);

  MockAppleKeychain* mutable_this = const_cast<MockAppleKeychain*>(this);
  mutable_this->SetTestDataBytes(key, kSecServerItemAttr, serverName,
                                 serverNameLength);
  mutable_this->SetTestDataBytes(key, kSecSecurityDomainItemAttr,
                                 securityDomain, securityDomainLength);
  mutable_this->SetTestDataBytes(key, kSecAccountItemAttr, accountName,
                                 accountNameLength);
  mutable_this->SetTestDataBytes(key, kSecPathItemAttr, path, pathLength);
  mutable_this->SetTestDataPort(key, port);
  mutable_this->SetTestDataProtocol(key, protocol);
  mutable_this->SetTestDataAuthType(key, authenticationType);
  mutable_this->SetTestDataPasswordBytes(key, passwordData,
                                         passwordLength);
  base::Time::Exploded exploded_time;
  base::Time::Now().UTCExplode(&exploded_time);
  char time_string[128];
  snprintf(time_string, sizeof(time_string), "%04d%02d%02d%02d%02d%02dZ",
           exploded_time.year, exploded_time.month, exploded_time.day_of_month,
           exploded_time.hour, exploded_time.minute, exploded_time.second);
  mutable_this->SetTestDataString(key, kSecCreationDateItemAttr, time_string);

  added_via_api_.insert(key);

  if (itemRef) {
    *itemRef = reinterpret_cast<SecKeychainItemRef>(key + 1);
    ++keychain_item_copy_count_;
  }
  return noErr;
}

OSStatus MockAppleKeychain::SearchCopyNext(SecKeychainSearchRef searchRef,
                                           SecKeychainItemRef* itemRef) const {
  if (remaining_search_results_.empty())
    return errSecItemNotFound;
  MockKeychainItemType key = remaining_search_results_.front();
  remaining_search_results_.erase(remaining_search_results_.begin());
  *itemRef = reinterpret_cast<SecKeychainItemRef>(key + 1);
  ++keychain_item_copy_count_;
  return noErr;
}

void MockAppleKeychain::Free(CFTypeRef ref) const {
  if (!ref)
    return;

  if (ref == kDummySearchRef) {
    --search_copy_count_;
  } else {
    --keychain_item_copy_count_;
  }
}

int MockAppleKeychain::UnfreedSearchCount() const {
  return search_copy_count_;
}

int MockAppleKeychain::UnfreedKeychainItemCount() const {
  return keychain_item_copy_count_;
}

int MockAppleKeychain::UnfreedAttributeDataCount() const {
  return attribute_data_copy_count_;
}

bool MockAppleKeychain::CreatorCodesSetForAddedItems() const {
  for (std::set<MockKeychainItemType>::const_iterator
           i = added_via_api_.begin();
       i != added_via_api_.end();
       ++i) {
    SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[*i],
                                                       kSecCreatorItemAttr);
    OSType* data = static_cast<OSType*>(attribute->data);
    if (*data == 0)
      return false;
  }
  return true;
}

void MockAppleKeychain::AddTestItem(const KeychainTestData& item_data) {
  MockKeychainItemType key = next_item_key_++;

  InitializeKeychainData(key);
  SetTestDataAuthType(key, item_data.auth_type);
  SetTestDataString(key, kSecServerItemAttr, item_data.server);
  SetTestDataProtocol(key, item_data.protocol);
  SetTestDataString(key, kSecPathItemAttr, item_data.path);
  SetTestDataPort(key, item_data.port);
  SetTestDataString(key, kSecSecurityDomainItemAttr,
                    item_data.security_domain);
  SetTestDataString(key, kSecCreationDateItemAttr, item_data.creation_date);
  SetTestDataString(key, kSecAccountItemAttr, item_data.username);
  SetTestDataPasswordString(key, item_data.password);
  SetTestDataNegativeItem(key, item_data.negative_item);
}

}  // namespace crypto

/* [<][>][^][v][top][bottom][index][help] */