// Copyright (c) 2009 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. #ifndef CHROME_BROWSER_KEYCHAIN_MOCK_MAC_H_ #define CHROME_BROWSER_KEYCHAIN_MOCK_MAC_H_ #pragma once #include <set> #include <string> #include <vector> #include "chrome/browser/keychain_mac.h" // Mock Keychain wrapper for testing code that interacts with the OS Keychain. // The basic idea of this mock is that it has a static array of data, and // SecKeychainItemRef values are just indexes into that array (offset by 1 to // prevent problems with clients that null-check refs), cast to pointers. // // Note that "const" is pretty much meaningless for this class; the const-ness // of MacKeychain doesn't apply to the actual keychain data, so all of the Mock // data is mutable; don't assume that it won't change over the life of tests. class MockKeychain : public MacKeychain { public: // Create a Mock Keychain capable of holding item_capacity keychain items. explicit MockKeychain(unsigned int item_capacity); virtual ~MockKeychain(); virtual OSStatus ItemCopyAttributesAndData( SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info, SecItemClass *itemClass, SecKeychainAttributeList **attrList, UInt32 *length, void **outData) const; // Pass "fail_me" as the data to get errSecAuthFailed. virtual OSStatus ItemModifyAttributesAndData( SecKeychainItemRef itemRef, const SecKeychainAttributeList *attrList, UInt32 length, const void *data) const; virtual OSStatus ItemFreeAttributesAndData(SecKeychainAttributeList *attrList, void *data) const; virtual OSStatus ItemDelete(SecKeychainItemRef itemRef) const; virtual OSStatus SearchCreateFromAttributes( CFTypeRef keychainOrArray, SecItemClass itemClass, const SecKeychainAttributeList *attrList, SecKeychainSearchRef *searchRef) const; virtual OSStatus SearchCopyNext(SecKeychainSearchRef searchRef, SecKeychainItemRef *itemRef) const; // If there are unused slots in the Mock Keychain's capacity, the new item // will use the first free one, otherwise it will stomp the last item. // Pass "some.domain.com" as the serverName to get errSecDuplicateItem. virtual OSStatus 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; virtual OSStatus FindGenericPassword(CFTypeRef keychainOrArray, UInt32 serviceNameLength, const char *serviceName, UInt32 accountNameLength, const char *accountName, UInt32 *passwordLength, void **passwordData, SecKeychainItemRef *itemRef) const; virtual OSStatus ItemFreeContent(SecKeychainAttributeList *attrList, void *data) const; virtual OSStatus AddGenericPassword(SecKeychainRef keychain, UInt32 serviceNameLength, const char *serviceName, UInt32 accountNameLength, const char *accountName, UInt32 passwordLength, const void *passwordData, SecKeychainItemRef *itemRef) const; virtual void Free(CFTypeRef ref) const; // Return the counts of objects returned by Create/Copy functions but never // Free'd as they should have been. int UnfreedSearchCount() const; int UnfreedKeychainItemCount() const; int UnfreedAttributeDataCount() const; // Returns true if all items added with AddInternetPassword have a creator // code set. bool CreatorCodesSetForAddedItems() const; struct KeychainTestData { const SecAuthenticationType auth_type; const char* server; const SecProtocolType protocol; const char* path; const UInt32 port; const char* security_domain; const char* creation_date; const char* username; const char* password; const bool negative_item; }; // Adds a keychain item with the given info to the test set. void AddTestItem(const KeychainTestData& item_data); // |FindGenericPassword()| can return different results depending on user // interaction with the system Keychain. For mocking purposes we allow the // user of this class to specify the result code of the // |FindGenericPassword()| call so we can simulate the result of different // user interactions. void set_find_generic_result(OSStatus result) { find_generic_result_ = result; } // Returns the true if |AddGenericPassword()| was called. bool called_add_generic() const { return called_add_generic_; } // Returns the value of the password set when |AddGenericPassword()| was // called. std::string add_generic_password() const { return add_generic_password_; } // Returns the number of allocations - deallocations for password data. int password_data_count() const { return password_data_count_; } private: // Sets the data and length of |tag| in the item-th test item. void SetTestDataBytes(int item, UInt32 tag, const void* data, size_t length); // Sets the data and length of |tag| in the item-th test item based on // |value|. The null-terminator will not be included; the Keychain Services // docs don't indicate whether it is or not, so clients should not assume // that it will be. void SetTestDataString(int item, UInt32 tag, const char* value); // Sets the data of the corresponding attribute of the item-th test item to // |value|. Assumes that the space has alread been allocated, and the length // set. void SetTestDataPort(int item, UInt32 value); void SetTestDataProtocol(int item, SecProtocolType value); void SetTestDataAuthType(int item, SecAuthenticationType value); void SetTestDataNegativeItem(int item, Boolean value); void SetTestDataCreator(int item, OSType value); // Sets the password data and length for the item-th test item. void SetTestDataPasswordBytes(int item, const void* data, size_t length); // Sets the password for the item-th test item. As with SetTestDataString, // the data will not be null-terminated. void SetTestDataPasswordString(int item, const char* value); // Returns the address of the attribute in attribute_list with tag |tag|. static SecKeychainAttribute* AttributeWithTag( const SecKeychainAttributeList& attribute_list, UInt32 tag); static const int kDummySearchRef = 1000; typedef struct { void* data; UInt32 length; } KeychainPasswordData; SecKeychainAttributeList* keychain_attr_list_; KeychainPasswordData* keychain_data_; unsigned int item_capacity_; mutable unsigned int item_count_; // Tracks the items that should be returned in subsequent calls to // SearchCopyNext, based on the last call to SearchCreateFromAttributes. // We can't handle multiple active searches, since we don't track the search // ref we return, but we don't need to for our mocking. mutable std::vector<unsigned int> remaining_search_results_; // Track copies and releases to make sure they balance. Really these should // be maps to track per item, but this should be good enough to catch // real mistakes. mutable int search_copy_count_; mutable int keychain_item_copy_count_; mutable int attribute_data_copy_count_; // Tracks which items (by index) were added with AddInternetPassword. mutable std::set<unsigned int> added_via_api_; // Result code for the |FindGenericPassword()| method. OSStatus find_generic_result_; // Records whether |AddGenericPassword()| gets called. mutable bool called_add_generic_; // Tracks the allocations and frees of password data in |FindGenericPassword| // and |ItemFreeContent|. mutable unsigned int password_data_count_; // Records the password being set when |AddGenericPassword()| gets called. mutable std::string add_generic_password_; }; #endif // CHROME_BROWSER_KEYCHAIN_MOCK_MAC_H_