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. 128 void set_find_generic_result(OSStatus result) { 129 find_generic_result_ = result; 130 } 131 132 // Returns the true if |AddGenericPassword()| was called. 133 bool called_add_generic() const { return called_add_generic_; } 134 135 // Returns the value of the password set when |AddGenericPassword()| was 136 // called. 137 std::string add_generic_password() const { return add_generic_password_; } 138 139 // Returns the number of allocations - deallocations for password data. 140 int password_data_count() const { return password_data_count_; } 141 142 private: 143 144 // Type used for the keys in the std::map(s) and MockAppleKeychain items. 145 typedef uintptr_t MockKeychainItemType; 146 147 // Type of the map holding the mock keychain attributes. 148 typedef std::map<MockKeychainItemType, SecKeychainAttributeList> 149 MockKeychainAttributesMap; 150 151 #if !defined(OS_IOS) 152 // Returns true if the keychain already contains a password that matches the 153 // attributes provided. 154 bool AlreadyContainsInternetPassword( 155 UInt32 serverNameLength, 156 const char* serverName, 157 UInt32 securityDomainLength, 158 const char* securityDomain, 159 UInt32 accountNameLength, 160 const char* accountName, 161 UInt32 pathLength, 162 const char* path, 163 UInt16 port, 164 SecProtocolType protocol, 165 SecAuthenticationType authenticationType) const; 166 // Initializes storage for keychain data at |key|. 167 void InitializeKeychainData(MockKeychainItemType key) const; 168 // Sets the data and length of |tag| in the item-th test item. 169 void SetTestDataBytes( 170 MockKeychainItemType item, 171 UInt32 tag, 172 const void* data, 173 size_t length); 174 // Sets the data and length of |tag| in the item-th test item based on 175 // |value|. The null-terminator will not be included; the Keychain Services 176 // docs don't indicate whether it is or not, so clients should not assume 177 // that it will be. 178 void SetTestDataString(MockKeychainItemType item, 179 UInt32 tag, 180 const char* value); 181 // Sets the data of the corresponding attribute of the item-th test item to 182 // |value|. Assumes that the space has alread been allocated, and the length 183 // set. 184 void SetTestDataPort(MockKeychainItemType item, UInt32 value); 185 void SetTestDataProtocol(MockKeychainItemType item, SecProtocolType value); 186 void SetTestDataAuthType(MockKeychainItemType item, 187 SecAuthenticationType value); 188 void SetTestDataNegativeItem(MockKeychainItemType item, Boolean value); 189 void SetTestDataCreator(MockKeychainItemType item, OSType value); 190 // Sets the password data and length for the item-th test item. 191 void SetTestDataPasswordBytes(MockKeychainItemType item, 192 const void* data, 193 size_t length); 194 // Sets the password for the item-th test item. As with SetTestDataString, 195 // the data will not be null-terminated. 196 void SetTestDataPasswordString(MockKeychainItemType item, const char* value); 197 198 // Returns the address of the attribute in attribute_list with tag |tag|. 199 static SecKeychainAttribute* AttributeWithTag( 200 const SecKeychainAttributeList& attribute_list, 201 UInt32 tag); 202 203 static const SecKeychainSearchRef kDummySearchRef; 204 205 typedef struct KeychainPasswordData { 206 KeychainPasswordData() : data(NULL), length(0) {} 207 void* data; 208 UInt32 length; 209 } KeychainPasswordData; 210 211 // Mutable because the MockAppleKeychain API requires its internal keychain 212 // storage to be modifiable by users of this class. 213 mutable MockKeychainAttributesMap keychain_attr_list_; 214 mutable std::map<MockKeychainItemType, 215 KeychainPasswordData> keychain_data_; 216 mutable MockKeychainItemType next_item_key_; 217 218 // Tracks the items that should be returned in subsequent calls to 219 // SearchCopyNext, based on the last call to SearchCreateFromAttributes. 220 // We can't handle multiple active searches, since we don't track the search 221 // ref we return, but we don't need to for our mocking. 222 mutable std::vector<MockKeychainItemType> remaining_search_results_; 223 224 // Track copies and releases to make sure they balance. Really these should 225 // be maps to track per item, but this should be good enough to catch 226 // real mistakes. 227 mutable int search_copy_count_; 228 mutable int keychain_item_copy_count_; 229 mutable int attribute_data_copy_count_; 230 231 // Tracks which items (by key) were added with AddInternetPassword. 232 mutable std::set<MockKeychainItemType> added_via_api_; 233 #endif // !defined(OS_IOS) 234 235 // Result code for the |FindGenericPassword()| method. 236 OSStatus find_generic_result_; 237 238 // Records whether |AddGenericPassword()| gets called. 239 mutable bool called_add_generic_; 240 241 // Tracks the allocations and frees of password data in |FindGenericPassword| 242 // and |ItemFreeContent|. 243 mutable int password_data_count_; 244 245 // Records the password being set when |AddGenericPassword()| gets called. 246 mutable std::string add_generic_password_; 247 }; 248 249 } // namespace crypto 250 251 #endif // CRYPTO_MOCK_KEYCHAIN_MAC_H_ 252