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.
5 #include "base/logging.h"
7 #include "chrome/browser/mock_keychain_mac.h"
9 MockKeychain::MockKeychain()
11 search_copy_count_(0),
12 keychain_item_copy_count_(0),
13 attribute_data_copy_count_(0),
14 find_generic_result_(noErr
),
15 called_add_generic_(false),
16 password_data_count_(0) {}
18 void MockKeychain::InitializeKeychainData(unsigned int key
) const {
19 UInt32 tags
[] = { kSecAccountItemAttr
,
24 kSecAuthenticationTypeItemAttr
,
25 kSecSecurityDomainItemAttr
,
26 kSecCreationDateItemAttr
,
28 kSecCreatorItemAttr
};
29 keychain_attr_list_
[key
] = SecKeychainAttributeList();
30 keychain_data_
[key
] = KeychainPasswordData();
31 keychain_attr_list_
[key
].count
= arraysize(tags
);
32 keychain_attr_list_
[key
].attr
= static_cast<SecKeychainAttribute
*>(
33 calloc(keychain_attr_list_
[key
].count
, sizeof(SecKeychainAttribute
)));
34 for (unsigned int i
= 0; i
< keychain_attr_list_
[key
].count
; ++i
) {
35 keychain_attr_list_
[key
].attr
[i
].tag
= tags
[i
];
38 case kSecPortItemAttr
:
39 data_size
= sizeof(UInt32
);
41 case kSecProtocolItemAttr
:
42 data_size
= sizeof(SecProtocolType
);
44 case kSecAuthenticationTypeItemAttr
:
45 data_size
= sizeof(SecAuthenticationType
);
47 case kSecNegativeItemAttr
:
48 data_size
= sizeof(Boolean
);
50 case kSecCreatorItemAttr
:
51 data_size
= sizeof(OSType
);
55 keychain_attr_list_
[key
].attr
[i
].length
= data_size
;
56 keychain_attr_list_
[key
].attr
[i
].data
= calloc(1, data_size
);
61 MockKeychain::~MockKeychain() {
62 for (std::map
<unsigned int, SecKeychainAttributeList
>::iterator it
=
63 keychain_attr_list_
.begin(); it
!= keychain_attr_list_
.end(); ++it
) {
64 for (unsigned int i
= 0; i
< it
->second
.count
; ++i
) {
65 if (it
->second
.attr
[i
].data
) {
66 free(it
->second
.attr
[i
].data
);
69 free(it
->second
.attr
);
70 if (keychain_data_
[it
->first
].data
) {
71 free(keychain_data_
[it
->first
].data
);
74 keychain_attr_list_
.clear();
75 keychain_data_
.clear();
78 SecKeychainAttribute
* MockKeychain::AttributeWithTag(
79 const SecKeychainAttributeList
& attribute_list
, UInt32 tag
) {
80 int attribute_index
= -1;
81 for (unsigned int i
= 0; i
< attribute_list
.count
; ++i
) {
82 if (attribute_list
.attr
[i
].tag
== tag
) {
87 if (attribute_index
== -1) {
88 NOTREACHED() << "Unsupported attribute: " << tag
;
91 return &(attribute_list
.attr
[attribute_index
]);
94 void MockKeychain::SetTestDataBytes(int item
, UInt32 tag
, const void* data
,
96 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
98 attribute
->length
= length
;
100 if (attribute
->data
) {
101 free(attribute
->data
);
103 attribute
->data
= malloc(length
);
104 CHECK(attribute
->data
);
105 memcpy(attribute
->data
, data
, length
);
107 attribute
->data
= NULL
;
111 void MockKeychain::SetTestDataString(int item
, UInt32 tag
, const char* value
) {
112 SetTestDataBytes(item
, tag
, value
, value
? strlen(value
) : 0);
115 void MockKeychain::SetTestDataPort(int item
, UInt32 value
) {
116 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
118 UInt32
* data
= static_cast<UInt32
*>(attribute
->data
);
122 void MockKeychain::SetTestDataProtocol(int item
, SecProtocolType value
) {
123 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
124 kSecProtocolItemAttr
);
125 SecProtocolType
* data
= static_cast<SecProtocolType
*>(attribute
->data
);
129 void MockKeychain::SetTestDataAuthType(int item
, SecAuthenticationType value
) {
130 SecKeychainAttribute
* attribute
= AttributeWithTag(
131 keychain_attr_list_
[item
], kSecAuthenticationTypeItemAttr
);
132 SecAuthenticationType
* data
= static_cast<SecAuthenticationType
*>(
137 void MockKeychain::SetTestDataNegativeItem(int item
, Boolean value
) {
138 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
139 kSecNegativeItemAttr
);
140 Boolean
* data
= static_cast<Boolean
*>(attribute
->data
);
144 void MockKeychain::SetTestDataCreator(int item
, OSType value
) {
145 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
146 kSecCreatorItemAttr
);
147 OSType
* data
= static_cast<OSType
*>(attribute
->data
);
151 void MockKeychain::SetTestDataPasswordBytes(int item
, const void* data
,
153 keychain_data_
[item
].length
= length
;
155 if (keychain_data_
[item
].data
) {
156 free(keychain_data_
[item
].data
);
158 keychain_data_
[item
].data
= malloc(length
);
159 memcpy(keychain_data_
[item
].data
, data
, length
);
161 keychain_data_
[item
].data
= NULL
;
165 void MockKeychain::SetTestDataPasswordString(int item
, const char* value
) {
166 SetTestDataPasswordBytes(item
, value
, value
? strlen(value
) : 0);
169 OSStatus
MockKeychain::ItemCopyAttributesAndData(
170 SecKeychainItemRef itemRef
, SecKeychainAttributeInfo
*info
,
171 SecItemClass
*itemClass
, SecKeychainAttributeList
**attrList
,
172 UInt32
*length
, void **outData
) const {
174 unsigned int key
= reinterpret_cast<unsigned int>(itemRef
) - 1;
175 if (keychain_attr_list_
.find(key
) == keychain_attr_list_
.end()) {
176 return errSecInvalidItemRef
;
179 DCHECK(!itemClass
); // itemClass not implemented in the Mock.
181 *attrList
= &(keychain_attr_list_
[key
]);
184 *outData
= keychain_data_
[key
].data
;
186 *length
= keychain_data_
[key
].length
;
189 ++attribute_data_copy_count_
;
193 OSStatus
MockKeychain::ItemModifyAttributesAndData(
194 SecKeychainItemRef itemRef
, const SecKeychainAttributeList
*attrList
,
195 UInt32 length
, const void *data
) const {
197 const char* fail_trigger
= "fail_me";
198 if (length
== strlen(fail_trigger
) &&
199 memcmp(data
, fail_trigger
, length
) == 0) {
200 return errSecAuthFailed
;
203 unsigned int key
= reinterpret_cast<unsigned int>(itemRef
) - 1;
204 if (keychain_attr_list_
.find(key
) == keychain_attr_list_
.end()) {
205 return errSecInvalidItemRef
;
208 MockKeychain
* mutable_this
= const_cast<MockKeychain
*>(this);
210 for (UInt32 change_attr
= 0; change_attr
< attrList
->count
; ++change_attr
) {
211 if (attrList
->attr
[change_attr
].tag
== kSecCreatorItemAttr
) {
212 void* data
= attrList
->attr
[change_attr
].data
;
213 mutable_this
->SetTestDataCreator(key
, *(static_cast<OSType
*>(data
)));
220 mutable_this
->SetTestDataPasswordBytes(key
, data
, length
);
225 OSStatus
MockKeychain::ItemFreeAttributesAndData(
226 SecKeychainAttributeList
*attrList
,
228 --attribute_data_copy_count_
;
232 OSStatus
MockKeychain::ItemDelete(SecKeychainItemRef itemRef
) const {
233 unsigned int key
= reinterpret_cast<unsigned int>(itemRef
) - 1;
235 for (unsigned int i
= 0; i
< keychain_attr_list_
[key
].count
; ++i
) {
236 if (keychain_attr_list_
[key
].attr
[i
].data
) {
237 free(keychain_attr_list_
[key
].attr
[i
].data
);
240 free(keychain_attr_list_
[key
].attr
);
241 if (keychain_data_
[key
].data
) {
242 free(keychain_data_
[key
].data
);
245 keychain_attr_list_
.erase(key
);
246 keychain_data_
.erase(key
);
247 added_via_api_
.erase(key
);
251 OSStatus
MockKeychain::SearchCreateFromAttributes(
252 CFTypeRef keychainOrArray
, SecItemClass itemClass
,
253 const SecKeychainAttributeList
*attrList
,
254 SecKeychainSearchRef
*searchRef
) const {
255 // Figure out which of our mock items matches, and set up the array we'll use
256 // to generate results out of SearchCopyNext.
257 remaining_search_results_
.clear();
258 for (std::map
<unsigned int, SecKeychainAttributeList
>::const_iterator it
=
259 keychain_attr_list_
.begin(); it
!= keychain_attr_list_
.end(); ++it
) {
260 bool mock_item_matches
= true;
261 for (UInt32 search_attr
= 0; search_attr
< attrList
->count
; ++search_attr
) {
262 SecKeychainAttribute
* mock_attribute
=
263 AttributeWithTag(it
->second
, attrList
->attr
[search_attr
].tag
);
264 if (mock_attribute
->length
!= attrList
->attr
[search_attr
].length
||
265 memcmp(mock_attribute
->data
, attrList
->attr
[search_attr
].data
,
266 attrList
->attr
[search_attr
].length
) != 0) {
267 mock_item_matches
= false;
271 if (mock_item_matches
) {
272 remaining_search_results_
.push_back(it
->first
);
277 *searchRef
= reinterpret_cast<SecKeychainSearchRef
>(kDummySearchRef
);
278 ++search_copy_count_
;
282 bool MockKeychain::AlreadyContainsInternetPassword(
283 UInt32 serverNameLength
, const char *serverName
,
284 UInt32 securityDomainLength
, const char *securityDomain
,
285 UInt32 accountNameLength
, const char *accountName
,
286 UInt32 pathLength
, const char *path
,
287 UInt16 port
, SecProtocolType protocol
,
288 SecAuthenticationType authenticationType
) const {
289 for (std::map
<unsigned int, SecKeychainAttributeList
>::const_iterator it
=
290 keychain_attr_list_
.begin(); it
!= keychain_attr_list_
.end(); ++it
) {
291 SecKeychainAttribute
* attribute
;
292 attribute
= AttributeWithTag(it
->second
, kSecServerItemAttr
);
293 if ((attribute
->length
!= serverNameLength
) ||
294 (attribute
->data
== NULL
&& *serverName
!= '\0') ||
295 (attribute
->data
!= NULL
&& *serverName
== '\0') ||
297 (const char*) attribute
->data
,
298 serverNameLength
) != 0) {
301 attribute
= AttributeWithTag(it
->second
, kSecSecurityDomainItemAttr
);
302 if ((attribute
->length
!= securityDomainLength
) ||
303 (attribute
->data
== NULL
&& *securityDomain
!= '\0') ||
304 (attribute
->data
!= NULL
&& *securityDomain
== '\0') ||
305 strncmp(securityDomain
,
306 (const char*) attribute
->data
,
307 securityDomainLength
) != 0) {
310 attribute
= AttributeWithTag(it
->second
, kSecAccountItemAttr
);
311 if ((attribute
->length
!= accountNameLength
) ||
312 (attribute
->data
== NULL
&& *accountName
!= '\0') ||
313 (attribute
->data
!= NULL
&& *accountName
== '\0') ||
315 (const char*) attribute
->data
,
316 accountNameLength
) != 0) {
319 attribute
= AttributeWithTag(it
->second
, kSecPathItemAttr
);
320 if ((attribute
->length
!= pathLength
) ||
321 (attribute
->data
== NULL
&& *path
!= '\0') ||
322 (attribute
->data
!= NULL
&& *path
== '\0') ||
324 (const char*) attribute
->data
,
328 attribute
= AttributeWithTag(it
->second
, kSecPortItemAttr
);
329 if ((attribute
->data
== NULL
) ||
330 (port
!= *(static_cast<UInt32
*>(attribute
->data
)))) {
333 attribute
= AttributeWithTag(it
->second
, kSecProtocolItemAttr
);
334 if ((attribute
->data
== NULL
) ||
335 (protocol
!= *(static_cast<SecProtocolType
*>(attribute
->data
)))) {
338 attribute
= AttributeWithTag(it
->second
, kSecAuthenticationTypeItemAttr
);
339 if ((attribute
->data
== NULL
) ||
340 (authenticationType
!=
341 *(static_cast<SecAuthenticationType
*>(attribute
->data
)))) {
344 // The keychain already has this item, since all fields other than the
351 OSStatus
MockKeychain::AddInternetPassword(
352 SecKeychainRef keychain
,
353 UInt32 serverNameLength
, const char *serverName
,
354 UInt32 securityDomainLength
, const char *securityDomain
,
355 UInt32 accountNameLength
, const char *accountName
,
356 UInt32 pathLength
, const char *path
,
357 UInt16 port
, SecProtocolType protocol
,
358 SecAuthenticationType authenticationType
,
359 UInt32 passwordLength
, const void *passwordData
,
360 SecKeychainItemRef
*itemRef
) const {
362 // Check for the magic duplicate item trigger.
363 if (strcmp(serverName
, "some.domain.com") == 0) {
364 return errSecDuplicateItem
;
367 // If the account already exists in the keychain, we don't add it.
368 if (AlreadyContainsInternetPassword(serverNameLength
, serverName
,
369 securityDomainLength
, securityDomain
,
370 accountNameLength
, accountName
,
373 authenticationType
)) {
374 return errSecDuplicateItem
;
377 // Pick the next unused slot.
378 unsigned int key
= next_item_key_
++;
380 // Initialize keychain data storage at the target location.
381 InitializeKeychainData(key
);
383 MockKeychain
* mutable_this
= const_cast<MockKeychain
*>(this);
384 mutable_this
->SetTestDataBytes(key
, kSecServerItemAttr
, serverName
,
386 mutable_this
->SetTestDataBytes(key
, kSecSecurityDomainItemAttr
,
387 securityDomain
, securityDomainLength
);
388 mutable_this
->SetTestDataBytes(key
, kSecAccountItemAttr
, accountName
,
390 mutable_this
->SetTestDataBytes(key
, kSecPathItemAttr
, path
, pathLength
);
391 mutable_this
->SetTestDataPort(key
, port
);
392 mutable_this
->SetTestDataProtocol(key
, protocol
);
393 mutable_this
->SetTestDataAuthType(key
, authenticationType
);
394 mutable_this
->SetTestDataPasswordBytes(key
, passwordData
,
396 base::Time::Exploded exploded_time
;
397 base::Time::Now().UTCExplode(&exploded_time
);
398 char time_string
[128];
399 snprintf(time_string
, sizeof(time_string
), "%04d%02d%02d%02d%02d%02dZ",
400 exploded_time
.year
, exploded_time
.month
, exploded_time
.day_of_month
,
401 exploded_time
.hour
, exploded_time
.minute
, exploded_time
.second
);
402 mutable_this
->SetTestDataString(key
, kSecCreationDateItemAttr
, time_string
);
404 added_via_api_
.insert(key
);
407 *itemRef
= reinterpret_cast<SecKeychainItemRef
>(key
+ 1);
408 ++keychain_item_copy_count_
;
413 OSStatus
MockKeychain::SearchCopyNext(SecKeychainSearchRef searchRef
,
414 SecKeychainItemRef
*itemRef
) const {
415 if (remaining_search_results_
.empty()) {
416 return errSecItemNotFound
;
418 unsigned int key
= remaining_search_results_
.front();
419 remaining_search_results_
.erase(remaining_search_results_
.begin());
420 *itemRef
= reinterpret_cast<SecKeychainItemRef
>(key
+ 1);
421 ++keychain_item_copy_count_
;
425 OSStatus
MockKeychain::FindGenericPassword(CFTypeRef keychainOrArray
,
426 UInt32 serviceNameLength
,
427 const char *serviceName
,
428 UInt32 accountNameLength
,
429 const char *accountName
,
430 UInt32
*passwordLength
,
432 SecKeychainItemRef
*itemRef
) const {
433 // When simulating |noErr| we return canned |passwordData| and
434 // |passwordLenght|. Otherwise, just return given code.
435 if (find_generic_result_
== noErr
) {
436 static char password
[] = "my_password";
438 DCHECK(passwordData
);
439 *passwordData
= static_cast<void*>(password
);
440 DCHECK(passwordLength
);
441 *passwordLength
= strlen(password
);
442 password_data_count_
++;
445 return find_generic_result_
;
448 OSStatus
MockKeychain::ItemFreeContent(SecKeychainAttributeList
*attrList
,
451 password_data_count_
--;
455 OSStatus
MockKeychain::AddGenericPassword(SecKeychainRef keychain
,
456 UInt32 serviceNameLength
,
457 const char *serviceName
,
458 UInt32 accountNameLength
,
459 const char *accountName
,
460 UInt32 passwordLength
,
461 const void *passwordData
,
462 SecKeychainItemRef
*itemRef
) const {
463 called_add_generic_
= true;
465 DCHECK(passwordLength
> 0);
466 DCHECK(passwordData
);
467 add_generic_password_
=
468 std::string(const_cast<char*>(static_cast<const char*>(passwordData
)),
473 void MockKeychain::Free(CFTypeRef ref
) const {
478 if (reinterpret_cast<int>(ref
) == kDummySearchRef
) {
479 --search_copy_count_
;
481 --keychain_item_copy_count_
;
485 int MockKeychain::UnfreedSearchCount() const {
486 return search_copy_count_
;
489 int MockKeychain::UnfreedKeychainItemCount() const {
490 return keychain_item_copy_count_
;
493 int MockKeychain::UnfreedAttributeDataCount() const {
494 return attribute_data_copy_count_
;
497 bool MockKeychain::CreatorCodesSetForAddedItems() const {
498 for (std::set
<unsigned int>::const_iterator i
= added_via_api_
.begin();
499 i
!= added_via_api_
.end(); ++i
) {
500 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[*i
],
501 kSecCreatorItemAttr
);
502 OSType
* data
= static_cast<OSType
*>(attribute
->data
);
510 void MockKeychain::AddTestItem(const KeychainTestData
& item_data
) {
511 unsigned int key
= next_item_key_
++;
513 InitializeKeychainData(key
);
514 SetTestDataAuthType(key
, item_data
.auth_type
);
515 SetTestDataString(key
, kSecServerItemAttr
, item_data
.server
);
516 SetTestDataProtocol(key
, item_data
.protocol
);
517 SetTestDataString(key
, kSecPathItemAttr
, item_data
.path
);
518 SetTestDataPort(key
, item_data
.port
);
519 SetTestDataString(key
, kSecSecurityDomainItemAttr
,
520 item_data
.security_domain
);
521 SetTestDataString(key
, kSecCreationDateItemAttr
, item_data
.creation_date
);
522 SetTestDataString(key
, kSecAccountItemAttr
, item_data
.username
);
523 SetTestDataPasswordString(key
, item_data
.password
);
524 SetTestDataNegativeItem(key
, item_data
.negative_item
);