// 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. #ifndef CRYPTO_MOCK_KEYCHAIN_MAC_H_ #define CRYPTO_MOCK_KEYCHAIN_MAC_H_ #include <stdint.h> #include <map> #include <set> #include <string> #include <vector> #include "base/compiler_specific.h" #include "crypto/apple_keychain.h" namespace crypto { // Mock Keychain wrapper for testing code that interacts with the OS X // Keychain. Implemented by storing SecKeychainAttributeList and // KeychainPasswordData values in separate mutable containers and // mapping them to integer keys. // // Note that "const" is pretty much meaningless for this class; the const-ness // of AppleKeychain 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 CRYPTO_EXPORT MockAppleKeychain : public AppleKeychain { public: MockAppleKeychain(); virtual ~MockAppleKeychain(); // AppleKeychain implementation. virtual OSStatus FindGenericPassword( CFTypeRef keychainOrArray, UInt32 serviceNameLength, const char* serviceName, UInt32 accountNameLength, const char* accountName, UInt32* passwordLength, void** passwordData, SecKeychainItemRef* itemRef) const OVERRIDE; virtual OSStatus ItemFreeContent(SecKeychainAttributeList* attrList, void* data) const OVERRIDE; virtual OSStatus AddGenericPassword( SecKeychainRef keychain, UInt32 serviceNameLength, const char* serviceName, UInt32 accountNameLength, const char* accountName, UInt32 passwordLength, const void* passwordData, SecKeychainItemRef* itemRef) const OVERRIDE; #if !defined(OS_IOS) virtual OSStatus ItemCopyAttributesAndData( SecKeychainItemRef itemRef, SecKeychainAttributeInfo* info, SecItemClass* itemClass, SecKeychainAttributeList** attrList, UInt32* length, void** outData) const OVERRIDE; // Pass "fail_me" as the data to get errSecAuthFailed. virtual OSStatus ItemModifyAttributesAndData( SecKeychainItemRef itemRef, const SecKeychainAttributeList* attrList, UInt32 length, const void* data) const OVERRIDE; virtual OSStatus ItemFreeAttributesAndData(SecKeychainAttributeList* attrList, void* data) const OVERRIDE; virtual OSStatus ItemDelete(SecKeychainItemRef itemRef) const OVERRIDE; virtual OSStatus SearchCreateFromAttributes( CFTypeRef keychainOrArray, SecItemClass itemClass, const SecKeychainAttributeList* attrList, SecKeychainSearchRef* searchRef) const OVERRIDE; virtual OSStatus SearchCopyNext(SecKeychainSearchRef searchRef, SecKeychainItemRef* itemRef) const OVERRIDE; // 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 OVERRIDE; virtual void Free(CFTypeRef ref) const OVERRIDE; // 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); #endif // !defined(OS_IOS) // |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: // Type used for the keys in the std::map(s) and MockAppleKeychain items. typedef uintptr_t MockKeychainItemType; // Type of the map holding the mock keychain attributes. typedef std::map<MockKeychainItemType, SecKeychainAttributeList> MockKeychainAttributesMap; #if !defined(OS_IOS) // Returns true if the keychain already contains a password that matches the // attributes provided. bool 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; // Initializes storage for keychain data at |key|. void InitializeKeychainData(MockKeychainItemType key) const; // Sets the data and length of |tag| in the item-th test item. void SetTestDataBytes( MockKeychainItemType 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(MockKeychainItemType 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(MockKeychainItemType item, UInt32 value); void SetTestDataProtocol(MockKeychainItemType item, SecProtocolType value); void SetTestDataAuthType(MockKeychainItemType item, SecAuthenticationType value); void SetTestDataNegativeItem(MockKeychainItemType item, Boolean value); void SetTestDataCreator(MockKeychainItemType item, OSType value); // Sets the password data and length for the item-th test item. void SetTestDataPasswordBytes(MockKeychainItemType 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(MockKeychainItemType 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 SecKeychainSearchRef kDummySearchRef; typedef struct KeychainPasswordData { KeychainPasswordData() : data(NULL), length(0) {} void* data; UInt32 length; } KeychainPasswordData; // Mutable because the MockAppleKeychain API requires its internal keychain // storage to be modifiable by users of this class. mutable MockKeychainAttributesMap keychain_attr_list_; mutable std::map<MockKeychainItemType, KeychainPasswordData> keychain_data_; mutable MockKeychainItemType next_item_key_; // 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<MockKeychainItemType> 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 key) were added with AddInternetPassword. mutable std::set<MockKeychainItemType> added_via_api_; #endif // !defined(OS_IOS) // 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 int password_data_count_; // Records the password being set when |AddGenericPassword()| gets called. mutable std::string add_generic_password_; }; } // namespace crypto #endif // CRYPTO_MOCK_KEYCHAIN_MAC_H_