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 #ifndef CRYPTO_MOCK_KEYCHAIN_MAC_H_ 6 #define CRYPTO_MOCK_KEYCHAIN_MAC_H_ 7 8 #include <stdint.h> 9 10 #include <map> 11 #include <set> 12 #include <string> 13 #include <vector> 14 15 #include "base/compiler_specific.h" 16 #include "crypto/apple_keychain.h" 17 18 namespace crypto { 19 20 // Mock Keychain wrapper for testing code that interacts with the OS X 21 // Keychain. Implemented by storing SecKeychainAttributeList and 22 // KeychainPasswordData values in separate mutable containers and 23 // mapping them to integer keys. 24 // 25 // Note that "const" is pretty much meaningless for this class; the const-ness 26 // of AppleKeychain doesn't apply to the actual keychain data, so all of the 27 // Mock data is mutable; don't assume that it won't change over the life of 28 // tests. 29 class CRYPTO_EXPORT MockAppleKeychain : public AppleKeychain { 30 public: 31 MockAppleKeychain(); 32 virtual ~MockAppleKeychain(); 33 34 // AppleKeychain implementation. 35 virtual OSStatus FindGenericPassword( 36 CFTypeRef keychainOrArray, 37 UInt32 serviceNameLength, 38 const char* serviceName, 39 UInt32 accountNameLength, 40 const char* accountName, 41 UInt32* passwordLength, 42 void** passwordData, 43 SecKeychainItemRef* itemRef) const OVERRIDE; 44 virtual OSStatus ItemFreeContent(SecKeychainAttributeList* attrList, 45 void* data) const OVERRIDE; 46 virtual OSStatus AddGenericPassword( 47 SecKeychainRef keychain, 48 UInt32 serviceNameLength, 49 const char* serviceName, 50 UInt32 accountNameLength, 51 const char* accountName, 52 UInt32 passwordLength, 53 const void* passwordData, 54 SecKeychainItemRef* itemRef) const OVERRIDE; 55 56 #if !defined(OS_IOS) 57 virtual OSStatus ItemCopyAttributesAndData( 58 SecKeychainItemRef itemRef, 59 SecKeychainAttributeInfo* info, 60 SecItemClass* itemClass, 61 SecKeychainAttributeList** attrList, 62 UInt32* length, 63 void** outData) const OVERRIDE; 64 // Pass "fail_me" as the data to get errSecAuthFailed. 65 virtual OSStatus ItemModifyAttributesAndData( 66 SecKeychainItemRef itemRef, 67 const SecKeychainAttributeList* attrList, 68 UInt32 length, 69 const void* data) const OVERRIDE; 70 virtual OSStatus ItemFreeAttributesAndData(SecKeychainAttributeList* attrList, 71 void* data) const OVERRIDE; 72 virtual OSStatus ItemDelete(SecKeychainItemRef itemRef) const OVERRIDE; 73 virtual OSStatus SearchCreateFromAttributes( 74 CFTypeRef keychainOrArray, 75 SecItemClass itemClass, 76 const SecKeychainAttributeList* attrList, 77 SecKeychainSearchRef* searchRef) const OVERRIDE; 78 virtual OSStatus SearchCopyNext(SecKeychainSearchRef searchRef, 79 SecKeychainItemRef* itemRef) const OVERRIDE; 80 // Pass "some.domain.com" as the serverName to get errSecDuplicateItem. 81 virtual OSStatus AddInternetPassword( 82 SecKeychainRef keychain, 83 UInt32 serverNameLength, 84 const char* serverName, 85 UInt32 securityDomainLength, 86 const char* securityDomain, 87 UInt32 accountNameLength, 88 const char* accountName, 89 UInt32 pathLength, const char* path, 90 UInt16 port, SecProtocolType protocol, 91 SecAuthenticationType authenticationType, 92 UInt32 passwordLength, 93 const void* passwordData, 94 SecKeychainItemRef* itemRef) const OVERRIDE; 95 virtual void Free(CFTypeRef ref) const OVERRIDE; 96 97 // Return the counts of objects returned by Create/Copy functions but never 98 // Free'd as they should have been. 99 int UnfreedSearchCount() const; 100 int UnfreedKeychainItemCount() const; 101 int UnfreedAttributeDataCount() const; 102 103 // Returns true if all items added with AddInternetPassword have a creator 104 // code set. 105 bool CreatorCodesSetForAddedItems() const; 106 107 struct KeychainTestData { 108 const SecAuthenticationType auth_type; 109 const char* server; 110 const SecProtocolType protocol; 111 const char* path; 112 const UInt32 port; 113 const char* security_domain; 114 const char* creation_date; 115 const char* username; 116 const char* password; 117 const bool negative_item; 118 }; 119 // Adds a keychain item with the given info to the test set. 120 void AddTestItem(const KeychainTestData& item_data); 121 #endif // !defined(OS_IOS) 122 123 // |FindGenericPassword()| can return different results depending on user 124 // interaction with the system Keychain. For mocking purposes we allow the 125 // user of this class to specify the result code of the 126 // |FindGenericPassword()| call so we can simulate the result of different 127 // user interactions. set_find_generic_result(OSStatus result)128 void set_find_generic_result(OSStatus result) { 129 find_generic_result_ = result; 130 } 131 132 // Returns the true if |AddGenericPassword()| was called. called_add_generic()133 bool called_add_generic() const { return called_add_generic_; } 134 135 // Returns the value of the password set when |AddGenericPassword()| was 136 // called. add_generic_password()137 std::string add_generic_password() const { return add_generic_password_; } 138 139 // Returns the number of allocations - deallocations for password data. password_data_count()140 int password_data_count() const { return password_data_count_; } 141 142 private: 143 // Type used for the keys in the std::map(s) and MockAppleKeychain items. 144 typedef uintptr_t MockKeychainItemType; 145 146 // Type of the map holding the mock keychain attributes. 147 typedef std::map<MockKeychainItemType, SecKeychainAttributeList> 148 MockKeychainAttributesMap; 149 150 #if !defined(OS_IOS) 151 // Returns true if the keychain already contains a password that matches the 152 // attributes provided. 153 bool AlreadyContainsInternetPassword( 154 UInt32 serverNameLength, 155 const char* serverName, 156 UInt32 securityDomainLength, 157 const char* securityDomain, 158 UInt32 accountNameLength, 159 const char* accountName, 160 UInt32 pathLength, 161 const char* path, 162 UInt16 port, 163 SecProtocolType protocol, 164 SecAuthenticationType authenticationType) const; 165 // Initializes storage for keychain data at |key|. 166 void InitializeKeychainData(MockKeychainItemType key) const; 167 // Sets the data and length of |tag| in the item-th test item. 168 void SetTestDataBytes( 169 MockKeychainItemType item, 170 UInt32 tag, 171 const void* data, 172 size_t length); 173 // Sets the data and length of |tag| in the item-th test item based on 174 // |value|. The null-terminator will not be included; the Keychain Services 175 // docs don't indicate whether it is or not, so clients should not assume 176 // that it will be. 177 void SetTestDataString(MockKeychainItemType item, 178 UInt32 tag, 179 const char* value); 180 // Sets the data of the corresponding attribute of the item-th test item to 181 // |value|. Assumes that the space has alread been allocated, and the length 182 // set. 183 void SetTestDataPort(MockKeychainItemType item, UInt32 value); 184 void SetTestDataProtocol(MockKeychainItemType item, SecProtocolType value); 185 void SetTestDataAuthType(MockKeychainItemType item, 186 SecAuthenticationType value); 187 void SetTestDataNegativeItem(MockKeychainItemType item, Boolean value); 188 void SetTestDataCreator(MockKeychainItemType item, OSType value); 189 // Sets the password data and length for the item-th test item. 190 void SetTestDataPasswordBytes(MockKeychainItemType item, 191 const void* data, 192 size_t length); 193 // Sets the password for the item-th test item. As with SetTestDataString, 194 // the data will not be null-terminated. 195 void SetTestDataPasswordString(MockKeychainItemType item, const char* value); 196 197 // Returns the address of the attribute in attribute_list with tag |tag|. 198 static SecKeychainAttribute* AttributeWithTag( 199 const SecKeychainAttributeList& attribute_list, 200 UInt32 tag); 201 202 static const SecKeychainSearchRef kDummySearchRef; 203 204 typedef struct KeychainPasswordData { KeychainPasswordDataKeychainPasswordData205 KeychainPasswordData() : data(NULL), length(0) {} 206 void* data; 207 UInt32 length; 208 } KeychainPasswordData; 209 210 // Mutable because the MockAppleKeychain API requires its internal keychain 211 // storage to be modifiable by users of this class. 212 mutable MockKeychainAttributesMap keychain_attr_list_; 213 mutable std::map<MockKeychainItemType, 214 KeychainPasswordData> keychain_data_; 215 mutable MockKeychainItemType next_item_key_; 216 217 // Tracks the items that should be returned in subsequent calls to 218 // SearchCopyNext, based on the last call to SearchCreateFromAttributes. 219 // We can't handle multiple active searches, since we don't track the search 220 // ref we return, but we don't need to for our mocking. 221 mutable std::vector<MockKeychainItemType> remaining_search_results_; 222 223 // Track copies and releases to make sure they balance. Really these should 224 // be maps to track per item, but this should be good enough to catch 225 // real mistakes. 226 mutable int search_copy_count_; 227 mutable int keychain_item_copy_count_; 228 mutable int attribute_data_copy_count_; 229 230 // Tracks which items (by key) were added with AddInternetPassword. 231 mutable std::set<MockKeychainItemType> added_via_api_; 232 #endif // !defined(OS_IOS) 233 234 // Result code for the |FindGenericPassword()| method. 235 OSStatus find_generic_result_; 236 237 // Records whether |AddGenericPassword()| gets called. 238 mutable bool called_add_generic_; 239 240 // Tracks the allocations and frees of password data in |FindGenericPassword| 241 // and |ItemFreeContent|. 242 mutable int password_data_count_; 243 244 // Records the password being set when |AddGenericPassword()| gets called. 245 mutable std::string add_generic_password_; 246 }; 247 248 } // namespace crypto 249 250 #endif // CRYPTO_MOCK_KEYCHAIN_MAC_H_ 251