1 // Copyright (c) 2009 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 #include "base/logging.h"
6 #include "base/time.h"
7 #include "chrome/browser/keychain_mock_mac.h"
8
MockKeychain(unsigned int item_capacity)9 MockKeychain::MockKeychain(unsigned int item_capacity)
10 : item_capacity_(item_capacity), item_count_(0), search_copy_count_(0),
11 keychain_item_copy_count_(0), attribute_data_copy_count_(0),
12 find_generic_result_(noErr), called_add_generic_(false),
13 password_data_count_(0) {
14 UInt32 tags[] = { kSecAccountItemAttr,
15 kSecServerItemAttr,
16 kSecPortItemAttr,
17 kSecPathItemAttr,
18 kSecProtocolItemAttr,
19 kSecAuthenticationTypeItemAttr,
20 kSecSecurityDomainItemAttr,
21 kSecCreationDateItemAttr,
22 kSecNegativeItemAttr,
23 kSecCreatorItemAttr };
24
25 // Create the test keychain data storage.
26 keychain_attr_list_ = static_cast<SecKeychainAttributeList*>(
27 calloc(item_capacity_, sizeof(SecKeychainAttributeList)));
28 keychain_data_ = static_cast<KeychainPasswordData*>(
29 calloc(item_capacity_, sizeof(KeychainPasswordData)));
30 for (unsigned int i = 0; i < item_capacity_; ++i) {
31 keychain_attr_list_[i].count = arraysize(tags);
32 keychain_attr_list_[i].attr = static_cast<SecKeychainAttribute*>(
33 calloc(keychain_attr_list_[i].count, sizeof(SecKeychainAttribute)));
34 for (unsigned int j = 0; j < keychain_attr_list_[i].count; ++j) {
35 keychain_attr_list_[i].attr[j].tag = tags[j];
36 size_t data_size = 0;
37 switch (tags[j]) {
38 case kSecPortItemAttr:
39 data_size = sizeof(UInt32);
40 break;
41 case kSecProtocolItemAttr:
42 data_size = sizeof(SecProtocolType);
43 break;
44 case kSecAuthenticationTypeItemAttr:
45 data_size = sizeof(SecAuthenticationType);
46 break;
47 case kSecNegativeItemAttr:
48 data_size = sizeof(Boolean);
49 break;
50 case kSecCreatorItemAttr:
51 data_size = sizeof(OSType);
52 break;
53 }
54 if (data_size > 0) {
55 keychain_attr_list_[i].attr[j].length = data_size;
56 keychain_attr_list_[i].attr[j].data = calloc(1, data_size);
57 }
58 }
59 }
60 }
61
~MockKeychain()62 MockKeychain::~MockKeychain() {
63 for (unsigned int i = 0; i < item_capacity_; ++i) {
64 for (unsigned int j = 0; j < keychain_attr_list_[i].count; ++j) {
65 if (keychain_attr_list_[i].attr[j].data) {
66 free(keychain_attr_list_[i].attr[j].data);
67 }
68 }
69 free(keychain_attr_list_[i].attr);
70 if (keychain_data_[i].data) {
71 free(keychain_data_[i].data);
72 }
73 }
74 free(keychain_attr_list_);
75 free(keychain_data_);
76 }
77
78
AttributeWithTag(const SecKeychainAttributeList & attribute_list,UInt32 tag)79 SecKeychainAttribute* MockKeychain::AttributeWithTag(
80 const SecKeychainAttributeList& attribute_list, UInt32 tag) {
81 int attribute_index = -1;
82 for (unsigned int i = 0; i < attribute_list.count; ++i) {
83 if (attribute_list.attr[i].tag == tag) {
84 attribute_index = i;
85 break;
86 }
87 }
88 if (attribute_index == -1) {
89 NOTREACHED() << "Unsupported attribute: " << tag;
90 return NULL;
91 }
92 return &(attribute_list.attr[attribute_index]);
93 }
94
SetTestDataBytes(int item,UInt32 tag,const void * data,size_t length)95 void MockKeychain::SetTestDataBytes(int item, UInt32 tag, const void* data,
96 size_t length) {
97 SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
98 tag);
99 attribute->length = length;
100 if (length > 0) {
101 if (attribute->data) {
102 free(attribute->data);
103 }
104 attribute->data = malloc(length);
105 CHECK(attribute->data);
106 memcpy(attribute->data, data, length);
107 } else {
108 attribute->data = NULL;
109 }
110 }
111
SetTestDataString(int item,UInt32 tag,const char * value)112 void MockKeychain::SetTestDataString(int item, UInt32 tag, const char* value) {
113 SetTestDataBytes(item, tag, value, value ? strlen(value) : 0);
114 }
115
SetTestDataPort(int item,UInt32 value)116 void MockKeychain::SetTestDataPort(int item, UInt32 value) {
117 SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
118 kSecPortItemAttr);
119 UInt32* data = static_cast<UInt32*>(attribute->data);
120 *data = value;
121 }
122
SetTestDataProtocol(int item,SecProtocolType value)123 void MockKeychain::SetTestDataProtocol(int item, SecProtocolType value) {
124 SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
125 kSecProtocolItemAttr);
126 SecProtocolType* data = static_cast<SecProtocolType*>(attribute->data);
127 *data = value;
128 }
129
SetTestDataAuthType(int item,SecAuthenticationType value)130 void MockKeychain::SetTestDataAuthType(int item, SecAuthenticationType value) {
131 SecKeychainAttribute* attribute = AttributeWithTag(
132 keychain_attr_list_[item], kSecAuthenticationTypeItemAttr);
133 SecAuthenticationType* data = static_cast<SecAuthenticationType*>(
134 attribute->data);
135 *data = value;
136 }
137
SetTestDataNegativeItem(int item,Boolean value)138 void MockKeychain::SetTestDataNegativeItem(int item, Boolean value) {
139 SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
140 kSecNegativeItemAttr);
141 Boolean* data = static_cast<Boolean*>(attribute->data);
142 *data = value;
143 }
144
SetTestDataCreator(int item,OSType value)145 void MockKeychain::SetTestDataCreator(int item, OSType value) {
146 SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item],
147 kSecCreatorItemAttr);
148 OSType* data = static_cast<OSType*>(attribute->data);
149 *data = value;
150 }
151
SetTestDataPasswordBytes(int item,const void * data,size_t length)152 void MockKeychain::SetTestDataPasswordBytes(int item, const void* data,
153 size_t length) {
154 keychain_data_[item].length = length;
155 if (length > 0) {
156 if (keychain_data_[item].data) {
157 free(keychain_data_[item].data);
158 }
159 keychain_data_[item].data = malloc(length);
160 memcpy(keychain_data_[item].data, data, length);
161 } else {
162 keychain_data_[item].data = NULL;
163 }
164 }
165
SetTestDataPasswordString(int item,const char * value)166 void MockKeychain::SetTestDataPasswordString(int item, const char* value) {
167 SetTestDataPasswordBytes(item, value, value ? strlen(value) : 0);
168 }
169
ItemCopyAttributesAndData(SecKeychainItemRef itemRef,SecKeychainAttributeInfo * info,SecItemClass * itemClass,SecKeychainAttributeList ** attrList,UInt32 * length,void ** outData) const170 OSStatus MockKeychain::ItemCopyAttributesAndData(
171 SecKeychainItemRef itemRef, SecKeychainAttributeInfo *info,
172 SecItemClass *itemClass, SecKeychainAttributeList **attrList,
173 UInt32 *length, void **outData) const {
174 DCHECK(itemRef);
175 unsigned int item_index = reinterpret_cast<unsigned int>(itemRef) - 1;
176 if (item_index >= item_count_) {
177 return errSecInvalidItemRef;
178 }
179
180 DCHECK(!itemClass); // itemClass not implemented in the Mock.
181 if (attrList) {
182 *attrList = &(keychain_attr_list_[item_index]);
183 }
184 if (outData) {
185 *outData = keychain_data_[item_index].data;
186 DCHECK(length);
187 *length = keychain_data_[item_index].length;
188 }
189
190 ++attribute_data_copy_count_;
191 return noErr;
192 }
193
ItemModifyAttributesAndData(SecKeychainItemRef itemRef,const SecKeychainAttributeList * attrList,UInt32 length,const void * data) const194 OSStatus MockKeychain::ItemModifyAttributesAndData(
195 SecKeychainItemRef itemRef, const SecKeychainAttributeList *attrList,
196 UInt32 length, const void *data) const {
197 DCHECK(itemRef);
198 const char* fail_trigger = "fail_me";
199 if (length == strlen(fail_trigger) &&
200 memcmp(data, fail_trigger, length) == 0) {
201 return errSecAuthFailed;
202 }
203
204 unsigned int item_index = reinterpret_cast<unsigned int>(itemRef) - 1;
205 if (item_index >= item_count_) {
206 return errSecInvalidItemRef;
207 }
208
209 MockKeychain* mutable_this = const_cast<MockKeychain*>(this);
210 if (attrList) {
211 for (UInt32 change_attr = 0; change_attr < attrList->count; ++change_attr) {
212 if (attrList->attr[change_attr].tag == kSecCreatorItemAttr) {
213 void* data = attrList->attr[change_attr].data;
214 mutable_this->SetTestDataCreator(item_index,
215 *(static_cast<OSType*>(data)));
216 } else {
217 NOTIMPLEMENTED();
218 }
219 }
220 }
221 if (data) {
222 mutable_this->SetTestDataPasswordBytes(item_index, data, length);
223 }
224 return noErr;
225 }
226
ItemFreeAttributesAndData(SecKeychainAttributeList * attrList,void * data) const227 OSStatus MockKeychain::ItemFreeAttributesAndData(
228 SecKeychainAttributeList *attrList,
229 void *data) const {
230 --attribute_data_copy_count_;
231 return noErr;
232 }
233
ItemDelete(SecKeychainItemRef itemRef) const234 OSStatus MockKeychain::ItemDelete(SecKeychainItemRef itemRef) const {
235 unsigned int item_index = reinterpret_cast<unsigned int>(itemRef) - 1;
236 // The mock only supports deleting the last item.
237 if (item_index != item_count_ - 1) {
238 NOTIMPLEMENTED();
239 }
240 --item_count_;
241 return noErr;
242 }
243
SearchCreateFromAttributes(CFTypeRef keychainOrArray,SecItemClass itemClass,const SecKeychainAttributeList * attrList,SecKeychainSearchRef * searchRef) const244 OSStatus MockKeychain::SearchCreateFromAttributes(
245 CFTypeRef keychainOrArray, SecItemClass itemClass,
246 const SecKeychainAttributeList *attrList,
247 SecKeychainSearchRef *searchRef) const {
248 // Figure out which of our mock items matches, and set up the array we'll use
249 // to generate results out of SearchCopyNext.
250 remaining_search_results_.clear();
251 for (unsigned int mock_item = 0; mock_item < item_count_; ++mock_item) {
252 bool mock_item_matches = true;
253 for (UInt32 search_attr = 0; search_attr < attrList->count; ++search_attr) {
254 SecKeychainAttribute* mock_attribute =
255 AttributeWithTag(keychain_attr_list_[mock_item],
256 attrList->attr[search_attr].tag);
257 if (mock_attribute->length != attrList->attr[search_attr].length ||
258 memcmp(mock_attribute->data, attrList->attr[search_attr].data,
259 attrList->attr[search_attr].length) != 0) {
260 mock_item_matches = false;
261 break;
262 }
263 }
264 if (mock_item_matches) {
265 remaining_search_results_.push_back(mock_item);
266 }
267 }
268
269 DCHECK(searchRef);
270 *searchRef = reinterpret_cast<SecKeychainSearchRef>(kDummySearchRef);
271 ++search_copy_count_;
272 return noErr;
273 }
274
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) const275 OSStatus MockKeychain::AddInternetPassword(
276 SecKeychainRef keychain,
277 UInt32 serverNameLength, const char *serverName,
278 UInt32 securityDomainLength, const char *securityDomain,
279 UInt32 accountNameLength, const char *accountName,
280 UInt32 pathLength, const char *path,
281 UInt16 port, SecProtocolType protocol,
282 SecAuthenticationType authenticationType,
283 UInt32 passwordLength, const void *passwordData,
284 SecKeychainItemRef *itemRef) const {
285
286 // Check for the magic duplicate item trigger.
287 if (strcmp(serverName, "some.domain.com") == 0) {
288 return errSecDuplicateItem;
289 }
290
291 // Use empty slots until they run out, then just keep replacing the last item.
292 int target_item = (item_count_ == item_capacity_) ? item_capacity_ - 1
293 : item_count_++;
294
295 MockKeychain* mutable_this = const_cast<MockKeychain*>(this);
296 mutable_this->SetTestDataBytes(target_item, kSecServerItemAttr, serverName,
297 serverNameLength);
298 mutable_this->SetTestDataBytes(target_item, kSecSecurityDomainItemAttr,
299 securityDomain, securityDomainLength);
300 mutable_this->SetTestDataBytes(target_item, kSecAccountItemAttr, accountName,
301 accountNameLength);
302 mutable_this->SetTestDataBytes(target_item, kSecPathItemAttr, path,
303 pathLength);
304 mutable_this->SetTestDataPort(target_item, port);
305 mutable_this->SetTestDataProtocol(target_item, protocol);
306 mutable_this->SetTestDataAuthType(target_item, authenticationType);
307 mutable_this->SetTestDataPasswordBytes(target_item, passwordData,
308 passwordLength);
309 base::Time::Exploded exploded_time;
310 base::Time::Now().UTCExplode(&exploded_time);
311 char time_string[128];
312 snprintf(time_string, sizeof(time_string), "%04d%02d%02d%02d%02d%02dZ",
313 exploded_time.year, exploded_time.month, exploded_time.day_of_month,
314 exploded_time.hour, exploded_time.minute, exploded_time.second);
315 mutable_this->SetTestDataString(target_item, kSecCreationDateItemAttr,
316 time_string);
317
318 added_via_api_.insert(target_item);
319
320 if (itemRef) {
321 *itemRef = reinterpret_cast<SecKeychainItemRef>(target_item + 1);
322 ++keychain_item_copy_count_;
323 }
324 return noErr;
325 }
326
SearchCopyNext(SecKeychainSearchRef searchRef,SecKeychainItemRef * itemRef) const327 OSStatus MockKeychain::SearchCopyNext(SecKeychainSearchRef searchRef,
328 SecKeychainItemRef *itemRef) const {
329 if (remaining_search_results_.empty()) {
330 return errSecItemNotFound;
331 }
332 unsigned int index = remaining_search_results_.front();
333 remaining_search_results_.erase(remaining_search_results_.begin());
334 *itemRef = reinterpret_cast<SecKeychainItemRef>(index + 1);
335 ++keychain_item_copy_count_;
336 return noErr;
337 }
338
FindGenericPassword(CFTypeRef keychainOrArray,UInt32 serviceNameLength,const char * serviceName,UInt32 accountNameLength,const char * accountName,UInt32 * passwordLength,void ** passwordData,SecKeychainItemRef * itemRef) const339 OSStatus MockKeychain::FindGenericPassword(CFTypeRef keychainOrArray,
340 UInt32 serviceNameLength,
341 const char *serviceName,
342 UInt32 accountNameLength,
343 const char *accountName,
344 UInt32 *passwordLength,
345 void **passwordData,
346 SecKeychainItemRef *itemRef) const {
347 // When simulating |noErr| we return canned |passwordData| and
348 // |passwordLenght|. Otherwise, just return given code.
349 if (find_generic_result_ == noErr) {
350 static char password[] = "my_password";
351
352 DCHECK(passwordData);
353 *passwordData = static_cast<void*>(password);
354 DCHECK(passwordLength);
355 *passwordLength = strlen(password);
356 password_data_count_++;
357 }
358
359 return find_generic_result_;
360 }
361
ItemFreeContent(SecKeychainAttributeList * attrList,void * data) const362 OSStatus MockKeychain::ItemFreeContent(SecKeychainAttributeList *attrList,
363 void *data) const {
364 // No-op.
365 password_data_count_--;
366 return noErr;
367 }
368
AddGenericPassword(SecKeychainRef keychain,UInt32 serviceNameLength,const char * serviceName,UInt32 accountNameLength,const char * accountName,UInt32 passwordLength,const void * passwordData,SecKeychainItemRef * itemRef) const369 OSStatus MockKeychain::AddGenericPassword(SecKeychainRef keychain,
370 UInt32 serviceNameLength,
371 const char *serviceName,
372 UInt32 accountNameLength,
373 const char *accountName,
374 UInt32 passwordLength,
375 const void *passwordData,
376 SecKeychainItemRef *itemRef) const {
377 called_add_generic_ = true;
378
379 DCHECK(passwordLength > 0);
380 DCHECK(passwordData);
381 add_generic_password_ =
382 std::string(const_cast<char*>(static_cast<const char*>(passwordData)),
383 passwordLength);
384 return noErr;
385 }
386
Free(CFTypeRef ref) const387 void MockKeychain::Free(CFTypeRef ref) const {
388 if (!ref) {
389 return;
390 }
391
392 if (reinterpret_cast<int>(ref) == kDummySearchRef) {
393 --search_copy_count_;
394 } else {
395 --keychain_item_copy_count_;
396 }
397 }
398
UnfreedSearchCount() const399 int MockKeychain::UnfreedSearchCount() const {
400 return search_copy_count_;
401 }
402
UnfreedKeychainItemCount() const403 int MockKeychain::UnfreedKeychainItemCount() const {
404 return keychain_item_copy_count_;
405 }
406
UnfreedAttributeDataCount() const407 int MockKeychain::UnfreedAttributeDataCount() const {
408 return attribute_data_copy_count_;
409 }
410
CreatorCodesSetForAddedItems() const411 bool MockKeychain::CreatorCodesSetForAddedItems() const {
412 for (std::set<unsigned int>::const_iterator i = added_via_api_.begin();
413 i != added_via_api_.end(); ++i) {
414 SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[*i],
415 kSecCreatorItemAttr);
416 OSType* data = static_cast<OSType*>(attribute->data);
417 if (*data == 0) {
418 return false;
419 }
420 }
421 return true;
422 }
423
AddTestItem(const KeychainTestData & item_data)424 void MockKeychain::AddTestItem(const KeychainTestData& item_data) {
425 unsigned int index = item_count_++;
426 CHECK(index < item_capacity_);
427
428 SetTestDataAuthType(index, item_data.auth_type);
429 SetTestDataString(index, kSecServerItemAttr, item_data.server);
430 SetTestDataProtocol(index, item_data.protocol);
431 SetTestDataString(index, kSecPathItemAttr, item_data.path);
432 SetTestDataPort(index, item_data.port);
433 SetTestDataString(index, kSecSecurityDomainItemAttr,
434 item_data.security_domain);
435 SetTestDataString(index, kSecCreationDateItemAttr, item_data.creation_date);
436 SetTestDataString(index, kSecAccountItemAttr, item_data.username);
437 SetTestDataPasswordString(index, item_data.password);
438 SetTestDataNegativeItem(index, item_data.negative_item);
439 }
440