Torne (Richard Coles) | 5821806 | 2012-11-14 11:43:16 +0000 | [diff] [blame] | 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_ |