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 "crypto/mock_keychain_mac.h"
12 const SecKeychainSearchRef
MockKeychain::kDummySearchRef
=
13 reinterpret_cast<SecKeychainSearchRef
>(1000);
15 MockKeychain::MockKeychain()
17 search_copy_count_(0),
18 keychain_item_copy_count_(0),
19 attribute_data_copy_count_(0),
20 find_generic_result_(noErr
),
21 called_add_generic_(false),
22 password_data_count_(0) {}
24 void MockKeychain::InitializeKeychainData(MockKeychainItemType key
) const {
25 UInt32 tags
[] = { kSecAccountItemAttr
,
30 kSecAuthenticationTypeItemAttr
,
31 kSecSecurityDomainItemAttr
,
32 kSecCreationDateItemAttr
,
34 kSecCreatorItemAttr
};
35 keychain_attr_list_
[key
] = SecKeychainAttributeList();
36 keychain_data_
[key
] = KeychainPasswordData();
37 keychain_attr_list_
[key
].count
= arraysize(tags
);
38 keychain_attr_list_
[key
].attr
= static_cast<SecKeychainAttribute
*>(
39 calloc(keychain_attr_list_
[key
].count
, sizeof(SecKeychainAttribute
)));
40 for (unsigned int i
= 0; i
< keychain_attr_list_
[key
].count
; ++i
) {
41 keychain_attr_list_
[key
].attr
[i
].tag
= tags
[i
];
44 case kSecPortItemAttr
:
45 data_size
= sizeof(UInt32
);
47 case kSecProtocolItemAttr
:
48 data_size
= sizeof(SecProtocolType
);
50 case kSecAuthenticationTypeItemAttr
:
51 data_size
= sizeof(SecAuthenticationType
);
53 case kSecNegativeItemAttr
:
54 data_size
= sizeof(Boolean
);
56 case kSecCreatorItemAttr
:
57 data_size
= sizeof(OSType
);
61 keychain_attr_list_
[key
].attr
[i
].length
= data_size
;
62 keychain_attr_list_
[key
].attr
[i
].data
= calloc(1, data_size
);
67 MockKeychain::~MockKeychain() {
68 for (std::map
<MockKeychainItemType
, SecKeychainAttributeList
>::iterator it
=
69 keychain_attr_list_
.begin(); it
!= keychain_attr_list_
.end(); ++it
) {
70 for (unsigned int i
= 0; i
< it
->second
.count
; ++i
) {
71 if (it
->second
.attr
[i
].data
)
72 free(it
->second
.attr
[i
].data
);
74 free(it
->second
.attr
);
75 if (keychain_data_
[it
->first
].data
)
76 free(keychain_data_
[it
->first
].data
);
78 keychain_attr_list_
.clear();
79 keychain_data_
.clear();
82 SecKeychainAttribute
* MockKeychain::AttributeWithTag(
83 const SecKeychainAttributeList
& attribute_list
,
85 int attribute_index
= -1;
86 for (unsigned int i
= 0; i
< attribute_list
.count
; ++i
) {
87 if (attribute_list
.attr
[i
].tag
== tag
) {
92 if (attribute_index
== -1) {
93 NOTREACHED() << "Unsupported attribute: " << tag
;
96 return &(attribute_list
.attr
[attribute_index
]);
99 void MockKeychain::SetTestDataBytes(MockKeychainItemType item
,
103 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
105 attribute
->length
= length
;
108 free(attribute
->data
);
109 attribute
->data
= malloc(length
);
110 CHECK(attribute
->data
);
111 memcpy(attribute
->data
, data
, length
);
113 attribute
->data
= NULL
;
117 void MockKeychain::SetTestDataString(
118 MockKeychainItemType item
,
121 SetTestDataBytes(item
, tag
, value
, value
? strlen(value
) : 0);
124 void MockKeychain::SetTestDataPort(MockKeychainItemType item
, UInt32 value
) {
125 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
127 UInt32
* data
= static_cast<UInt32
*>(attribute
->data
);
131 void MockKeychain::SetTestDataProtocol(MockKeychainItemType item
,
132 SecProtocolType value
) {
133 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
134 kSecProtocolItemAttr
);
135 SecProtocolType
* data
= static_cast<SecProtocolType
*>(attribute
->data
);
139 void MockKeychain::SetTestDataAuthType(
140 MockKeychainItemType item
,
141 SecAuthenticationType value
) {
142 SecKeychainAttribute
* attribute
= AttributeWithTag(
143 keychain_attr_list_
[item
], kSecAuthenticationTypeItemAttr
);
144 SecAuthenticationType
* data
= static_cast<SecAuthenticationType
*>(
149 void MockKeychain::SetTestDataNegativeItem(MockKeychainItemType item
,
151 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
152 kSecNegativeItemAttr
);
153 Boolean
* data
= static_cast<Boolean
*>(attribute
->data
);
157 void MockKeychain::SetTestDataCreator(MockKeychainItemType item
, OSType value
) {
158 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
159 kSecCreatorItemAttr
);
160 OSType
* data
= static_cast<OSType
*>(attribute
->data
);
164 void MockKeychain::SetTestDataPasswordBytes(MockKeychainItemType item
,
167 keychain_data_
[item
].length
= length
;
169 if (keychain_data_
[item
].data
)
170 free(keychain_data_
[item
].data
);
171 keychain_data_
[item
].data
= malloc(length
);
172 memcpy(keychain_data_
[item
].data
, data
, length
);
174 keychain_data_
[item
].data
= NULL
;
178 void MockKeychain::SetTestDataPasswordString(
179 MockKeychainItemType item
,
181 SetTestDataPasswordBytes(item
, value
, value
? strlen(value
) : 0);
184 OSStatus
MockKeychain::ItemCopyAttributesAndData(
185 SecKeychainItemRef itemRef
,
186 SecKeychainAttributeInfo
* info
,
187 SecItemClass
* itemClass
,
188 SecKeychainAttributeList
** attrList
,
190 void** outData
) const {
192 MockKeychainItemType key
=
193 reinterpret_cast<MockKeychainItemType
>(itemRef
) - 1;
194 if (keychain_attr_list_
.find(key
) == keychain_attr_list_
.end())
195 return errSecInvalidItemRef
;
197 DCHECK(!itemClass
); // itemClass not implemented in the Mock.
199 *attrList
= &(keychain_attr_list_
[key
]);
201 *outData
= keychain_data_
[key
].data
;
203 *length
= keychain_data_
[key
].length
;
206 ++attribute_data_copy_count_
;
210 OSStatus
MockKeychain::ItemModifyAttributesAndData(
211 SecKeychainItemRef itemRef
,
212 const SecKeychainAttributeList
* attrList
,
214 const void* data
) const {
216 const char* fail_trigger
= "fail_me";
217 if (length
== strlen(fail_trigger
) &&
218 memcmp(data
, fail_trigger
, length
) == 0) {
219 return errSecAuthFailed
;
222 MockKeychainItemType key
=
223 reinterpret_cast<MockKeychainItemType
>(itemRef
) - 1;
224 if (keychain_attr_list_
.find(key
) == keychain_attr_list_
.end())
225 return errSecInvalidItemRef
;
227 MockKeychain
* mutable_this
= const_cast<MockKeychain
*>(this);
229 for (UInt32 change_attr
= 0; change_attr
< attrList
->count
; ++change_attr
) {
230 if (attrList
->attr
[change_attr
].tag
== kSecCreatorItemAttr
) {
231 void* data
= attrList
->attr
[change_attr
].data
;
232 mutable_this
->SetTestDataCreator(key
, *(static_cast<OSType
*>(data
)));
239 mutable_this
->SetTestDataPasswordBytes(key
, data
, length
);
243 OSStatus
MockKeychain::ItemFreeAttributesAndData(
244 SecKeychainAttributeList
* attrList
,
246 --attribute_data_copy_count_
;
250 OSStatus
MockKeychain::ItemDelete(SecKeychainItemRef itemRef
) const {
251 MockKeychainItemType key
=
252 reinterpret_cast<MockKeychainItemType
>(itemRef
) - 1;
254 for (unsigned int i
= 0; i
< keychain_attr_list_
[key
].count
; ++i
) {
255 if (keychain_attr_list_
[key
].attr
[i
].data
)
256 free(keychain_attr_list_
[key
].attr
[i
].data
);
258 free(keychain_attr_list_
[key
].attr
);
259 if (keychain_data_
[key
].data
)
260 free(keychain_data_
[key
].data
);
262 keychain_attr_list_
.erase(key
);
263 keychain_data_
.erase(key
);
264 added_via_api_
.erase(key
);
268 OSStatus
MockKeychain::SearchCreateFromAttributes(
269 CFTypeRef keychainOrArray
,
270 SecItemClass itemClass
,
271 const SecKeychainAttributeList
* attrList
,
272 SecKeychainSearchRef
* searchRef
) const {
273 // Figure out which of our mock items matches, and set up the array we'll use
274 // to generate results out of SearchCopyNext.
275 remaining_search_results_
.clear();
276 for (std::map
<MockKeychainItemType
, SecKeychainAttributeList
>::const_iterator
277 it
= keychain_attr_list_
.begin();
278 it
!= keychain_attr_list_
.end();
280 bool mock_item_matches
= true;
281 for (UInt32 search_attr
= 0; search_attr
< attrList
->count
; ++search_attr
) {
282 SecKeychainAttribute
* mock_attribute
=
283 AttributeWithTag(it
->second
, attrList
->attr
[search_attr
].tag
);
284 if (mock_attribute
->length
!= attrList
->attr
[search_attr
].length
||
285 memcmp(mock_attribute
->data
, attrList
->attr
[search_attr
].data
,
286 attrList
->attr
[search_attr
].length
) != 0) {
287 mock_item_matches
= false;
291 if (mock_item_matches
)
292 remaining_search_results_
.push_back(it
->first
);
296 *searchRef
= kDummySearchRef
;
297 ++search_copy_count_
;
301 bool MockKeychain::AlreadyContainsInternetPassword(
302 UInt32 serverNameLength
,
303 const char* serverName
,
304 UInt32 securityDomainLength
,
305 const char* securityDomain
,
306 UInt32 accountNameLength
,
307 const char* accountName
,
311 SecProtocolType protocol
,
312 SecAuthenticationType authenticationType
) const {
313 for (std::map
<MockKeychainItemType
, SecKeychainAttributeList
>::const_iterator
314 it
= keychain_attr_list_
.begin();
315 it
!= keychain_attr_list_
.end();
317 SecKeychainAttribute
* attribute
;
318 attribute
= AttributeWithTag(it
->second
, kSecServerItemAttr
);
319 if ((attribute
->length
!= serverNameLength
) ||
320 (attribute
->data
== NULL
&& *serverName
!= '\0') ||
321 (attribute
->data
!= NULL
&& *serverName
== '\0') ||
323 (const char*) attribute
->data
,
324 serverNameLength
) != 0) {
327 attribute
= AttributeWithTag(it
->second
, kSecSecurityDomainItemAttr
);
328 if ((attribute
->length
!= securityDomainLength
) ||
329 (attribute
->data
== NULL
&& *securityDomain
!= '\0') ||
330 (attribute
->data
!= NULL
&& *securityDomain
== '\0') ||
331 strncmp(securityDomain
,
332 (const char*) attribute
->data
,
333 securityDomainLength
) != 0) {
336 attribute
= AttributeWithTag(it
->second
, kSecAccountItemAttr
);
337 if ((attribute
->length
!= accountNameLength
) ||
338 (attribute
->data
== NULL
&& *accountName
!= '\0') ||
339 (attribute
->data
!= NULL
&& *accountName
== '\0') ||
341 (const char*) attribute
->data
,
342 accountNameLength
) != 0) {
345 attribute
= AttributeWithTag(it
->second
, kSecPathItemAttr
);
346 if ((attribute
->length
!= pathLength
) ||
347 (attribute
->data
== NULL
&& *path
!= '\0') ||
348 (attribute
->data
!= NULL
&& *path
== '\0') ||
350 (const char*) attribute
->data
,
354 attribute
= AttributeWithTag(it
->second
, kSecPortItemAttr
);
355 if ((attribute
->data
== NULL
) ||
356 (port
!= *(static_cast<UInt32
*>(attribute
->data
)))) {
359 attribute
= AttributeWithTag(it
->second
, kSecProtocolItemAttr
);
360 if ((attribute
->data
== NULL
) ||
361 (protocol
!= *(static_cast<SecProtocolType
*>(attribute
->data
)))) {
364 attribute
= AttributeWithTag(it
->second
, kSecAuthenticationTypeItemAttr
);
365 if ((attribute
->data
== NULL
) ||
366 (authenticationType
!=
367 *(static_cast<SecAuthenticationType
*>(attribute
->data
)))) {
370 // The keychain already has this item, since all fields other than the
377 OSStatus
MockKeychain::AddInternetPassword(
378 SecKeychainRef keychain
,
379 UInt32 serverNameLength
,
380 const char* serverName
,
381 UInt32 securityDomainLength
,
382 const char* securityDomain
,
383 UInt32 accountNameLength
,
384 const char* accountName
,
388 SecProtocolType protocol
,
389 SecAuthenticationType authenticationType
,
390 UInt32 passwordLength
,
391 const void* passwordData
,
392 SecKeychainItemRef
* itemRef
) const {
394 // Check for the magic duplicate item trigger.
395 if (strcmp(serverName
, "some.domain.com") == 0)
396 return errSecDuplicateItem
;
398 // If the account already exists in the keychain, we don't add it.
399 if (AlreadyContainsInternetPassword(serverNameLength
, serverName
,
400 securityDomainLength
, securityDomain
,
401 accountNameLength
, accountName
,
404 authenticationType
)) {
405 return errSecDuplicateItem
;
408 // Pick the next unused slot.
409 MockKeychainItemType key
= next_item_key_
++;
411 // Initialize keychain data storage at the target location.
412 InitializeKeychainData(key
);
414 MockKeychain
* mutable_this
= const_cast<MockKeychain
*>(this);
415 mutable_this
->SetTestDataBytes(key
, kSecServerItemAttr
, serverName
,
417 mutable_this
->SetTestDataBytes(key
, kSecSecurityDomainItemAttr
,
418 securityDomain
, securityDomainLength
);
419 mutable_this
->SetTestDataBytes(key
, kSecAccountItemAttr
, accountName
,
421 mutable_this
->SetTestDataBytes(key
, kSecPathItemAttr
, path
, pathLength
);
422 mutable_this
->SetTestDataPort(key
, port
);
423 mutable_this
->SetTestDataProtocol(key
, protocol
);
424 mutable_this
->SetTestDataAuthType(key
, authenticationType
);
425 mutable_this
->SetTestDataPasswordBytes(key
, passwordData
,
427 base::Time::Exploded exploded_time
;
428 base::Time::Now().UTCExplode(&exploded_time
);
429 char time_string
[128];
430 snprintf(time_string
, sizeof(time_string
), "%04d%02d%02d%02d%02d%02dZ",
431 exploded_time
.year
, exploded_time
.month
, exploded_time
.day_of_month
,
432 exploded_time
.hour
, exploded_time
.minute
, exploded_time
.second
);
433 mutable_this
->SetTestDataString(key
, kSecCreationDateItemAttr
, time_string
);
435 added_via_api_
.insert(key
);
438 *itemRef
= reinterpret_cast<SecKeychainItemRef
>(key
+ 1);
439 ++keychain_item_copy_count_
;
444 OSStatus
MockKeychain::SearchCopyNext(SecKeychainSearchRef searchRef
,
445 SecKeychainItemRef
* itemRef
) const {
446 if (remaining_search_results_
.empty())
447 return errSecItemNotFound
;
448 MockKeychainItemType key
= remaining_search_results_
.front();
449 remaining_search_results_
.erase(remaining_search_results_
.begin());
450 *itemRef
= reinterpret_cast<SecKeychainItemRef
>(key
+ 1);
451 ++keychain_item_copy_count_
;
455 OSStatus
MockKeychain::FindGenericPassword(CFTypeRef keychainOrArray
,
456 UInt32 serviceNameLength
,
457 const char* serviceName
,
458 UInt32 accountNameLength
,
459 const char* accountName
,
460 UInt32
* passwordLength
,
462 SecKeychainItemRef
* itemRef
) const {
463 // When simulating |noErr| we return canned |passwordData| and
464 // |passwordLenght|. Otherwise, just return given code.
465 if (find_generic_result_
== noErr
) {
466 static char password
[] = "my_password";
468 DCHECK(passwordData
);
469 *passwordData
= static_cast<void*>(password
);
470 DCHECK(passwordLength
);
471 *passwordLength
= strlen(password
);
472 password_data_count_
++;
475 return find_generic_result_
;
478 OSStatus
MockKeychain::ItemFreeContent(SecKeychainAttributeList
* attrList
,
481 password_data_count_
--;
485 OSStatus
MockKeychain::AddGenericPassword(SecKeychainRef keychain
,
486 UInt32 serviceNameLength
,
487 const char* serviceName
,
488 UInt32 accountNameLength
,
489 const char* accountName
,
490 UInt32 passwordLength
,
491 const void* passwordData
,
492 SecKeychainItemRef
* itemRef
) const {
493 called_add_generic_
= true;
495 DCHECK(passwordLength
> 0);
496 DCHECK(passwordData
);
497 add_generic_password_
=
498 std::string(const_cast<char*>(static_cast<const char*>(passwordData
)),
503 void MockKeychain::Free(CFTypeRef ref
) const {
507 if (ref
== kDummySearchRef
) {
508 --search_copy_count_
;
510 --keychain_item_copy_count_
;
514 int MockKeychain::UnfreedSearchCount() const {
515 return search_copy_count_
;
518 int MockKeychain::UnfreedKeychainItemCount() const {
519 return keychain_item_copy_count_
;
522 int MockKeychain::UnfreedAttributeDataCount() const {
523 return attribute_data_copy_count_
;
526 bool MockKeychain::CreatorCodesSetForAddedItems() const {
527 for (std::set
<MockKeychainItemType
>::const_iterator
528 i
= added_via_api_
.begin();
529 i
!= added_via_api_
.end();
531 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[*i
],
532 kSecCreatorItemAttr
);
533 OSType
* data
= static_cast<OSType
*>(attribute
->data
);
540 void MockKeychain::AddTestItem(const KeychainTestData
& item_data
) {
541 MockKeychainItemType key
= next_item_key_
++;
543 InitializeKeychainData(key
);
544 SetTestDataAuthType(key
, item_data
.auth_type
);
545 SetTestDataString(key
, kSecServerItemAttr
, item_data
.server
);
546 SetTestDataProtocol(key
, item_data
.protocol
);
547 SetTestDataString(key
, kSecPathItemAttr
, item_data
.path
);
548 SetTestDataPort(key
, item_data
.port
);
549 SetTestDataString(key
, kSecSecurityDomainItemAttr
,
550 item_data
.security_domain
);
551 SetTestDataString(key
, kSecCreationDateItemAttr
, item_data
.creation_date
);
552 SetTestDataString(key
, kSecAccountItemAttr
, item_data
.username
);
553 SetTestDataPasswordString(key
, item_data
.password
);
554 SetTestDataNegativeItem(key
, item_data
.negative_item
);
557 } // namespace crypto