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"
6 #include "base/time/time.h"
7 #include "crypto/mock_apple_keychain.h"
12 const SecKeychainSearchRef
MockAppleKeychain::kDummySearchRef
=
13 reinterpret_cast<SecKeychainSearchRef
>(1000);
15 MockAppleKeychain::MockAppleKeychain()
18 search_copy_count_(0),
19 keychain_item_copy_count_(0),
20 attribute_data_copy_count_(0),
21 find_generic_result_(noErr
),
22 called_add_generic_(false),
23 password_data_count_(0) {}
25 void MockAppleKeychain::InitializeKeychainData(MockKeychainItemType key
) const {
26 UInt32 tags
[] = { kSecAccountItemAttr
,
31 kSecAuthenticationTypeItemAttr
,
32 kSecSecurityDomainItemAttr
,
33 kSecCreationDateItemAttr
,
35 kSecCreatorItemAttr
};
36 keychain_attr_list_
[key
] = SecKeychainAttributeList();
37 keychain_data_
[key
] = KeychainPasswordData();
38 keychain_attr_list_
[key
].count
= arraysize(tags
);
39 keychain_attr_list_
[key
].attr
= static_cast<SecKeychainAttribute
*>(
40 calloc(keychain_attr_list_
[key
].count
, sizeof(SecKeychainAttribute
)));
41 for (unsigned int i
= 0; i
< keychain_attr_list_
[key
].count
; ++i
) {
42 keychain_attr_list_
[key
].attr
[i
].tag
= tags
[i
];
45 case kSecPortItemAttr
:
46 data_size
= sizeof(UInt32
);
48 case kSecProtocolItemAttr
:
49 data_size
= sizeof(SecProtocolType
);
51 case kSecAuthenticationTypeItemAttr
:
52 data_size
= sizeof(SecAuthenticationType
);
54 case kSecNegativeItemAttr
:
55 data_size
= sizeof(Boolean
);
57 case kSecCreatorItemAttr
:
58 data_size
= sizeof(OSType
);
62 keychain_attr_list_
[key
].attr
[i
].length
= data_size
;
63 keychain_attr_list_
[key
].attr
[i
].data
= calloc(1, data_size
);
68 MockAppleKeychain::~MockAppleKeychain() {
69 for (MockKeychainAttributesMap::iterator it
= keychain_attr_list_
.begin();
70 it
!= keychain_attr_list_
.end();
72 for (unsigned int i
= 0; i
< it
->second
.count
; ++i
) {
73 if (it
->second
.attr
[i
].data
)
74 free(it
->second
.attr
[i
].data
);
76 free(it
->second
.attr
);
77 if (keychain_data_
[it
->first
].data
)
78 free(keychain_data_
[it
->first
].data
);
80 keychain_attr_list_
.clear();
81 keychain_data_
.clear();
84 SecKeychainAttribute
* MockAppleKeychain::AttributeWithTag(
85 const SecKeychainAttributeList
& attribute_list
,
87 int attribute_index
= -1;
88 for (unsigned int i
= 0; i
< attribute_list
.count
; ++i
) {
89 if (attribute_list
.attr
[i
].tag
== tag
) {
94 if (attribute_index
== -1) {
95 NOTREACHED() << "Unsupported attribute: " << tag
;
98 return &(attribute_list
.attr
[attribute_index
]);
101 void MockAppleKeychain::SetTestDataBytes(MockKeychainItemType item
,
105 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
107 attribute
->length
= length
;
110 free(attribute
->data
);
111 attribute
->data
= malloc(length
);
112 CHECK(attribute
->data
);
113 memcpy(attribute
->data
, data
, length
);
115 attribute
->data
= NULL
;
119 void MockAppleKeychain::SetTestDataString(MockKeychainItemType item
,
122 SetTestDataBytes(item
, tag
, value
, value
? strlen(value
) : 0);
125 void MockAppleKeychain::SetTestDataPort(MockKeychainItemType item
,
127 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
129 UInt32
* data
= static_cast<UInt32
*>(attribute
->data
);
133 void MockAppleKeychain::SetTestDataProtocol(MockKeychainItemType item
,
134 SecProtocolType value
) {
135 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
136 kSecProtocolItemAttr
);
137 SecProtocolType
* data
= static_cast<SecProtocolType
*>(attribute
->data
);
141 void MockAppleKeychain::SetTestDataAuthType(MockKeychainItemType item
,
142 SecAuthenticationType value
) {
143 SecKeychainAttribute
* attribute
= AttributeWithTag(
144 keychain_attr_list_
[item
], kSecAuthenticationTypeItemAttr
);
145 SecAuthenticationType
* data
= static_cast<SecAuthenticationType
*>(
150 void MockAppleKeychain::SetTestDataNegativeItem(MockKeychainItemType item
,
152 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
153 kSecNegativeItemAttr
);
154 Boolean
* data
= static_cast<Boolean
*>(attribute
->data
);
158 void MockAppleKeychain::SetTestDataCreator(MockKeychainItemType item
,
160 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[item
],
161 kSecCreatorItemAttr
);
162 OSType
* data
= static_cast<OSType
*>(attribute
->data
);
166 void MockAppleKeychain::SetTestDataPasswordBytes(MockKeychainItemType item
,
169 keychain_data_
[item
].length
= length
;
171 if (keychain_data_
[item
].data
)
172 free(keychain_data_
[item
].data
);
173 keychain_data_
[item
].data
= malloc(length
);
174 memcpy(keychain_data_
[item
].data
, data
, length
);
176 keychain_data_
[item
].data
= NULL
;
180 void MockAppleKeychain::SetTestDataPasswordString(MockKeychainItemType item
,
182 SetTestDataPasswordBytes(item
, value
, value
? strlen(value
) : 0);
185 OSStatus
MockAppleKeychain::ItemCopyAttributesAndData(
186 SecKeychainItemRef itemRef
,
187 SecKeychainAttributeInfo
* info
,
188 SecItemClass
* itemClass
,
189 SecKeychainAttributeList
** attrList
,
191 void** outData
) const {
193 MockKeychainItemType key
=
194 reinterpret_cast<MockKeychainItemType
>(itemRef
) - 1;
195 if (keychain_attr_list_
.find(key
) == keychain_attr_list_
.end())
196 return errSecInvalidItemRef
;
198 DCHECK(!itemClass
); // itemClass not implemented in the Mock.
199 if (locked_
&& outData
)
200 return errSecAuthFailed
;
203 *attrList
= &(keychain_attr_list_
[key
]);
205 *outData
= keychain_data_
[key
].data
;
207 *length
= keychain_data_
[key
].length
;
210 ++attribute_data_copy_count_
;
214 OSStatus
MockAppleKeychain::ItemModifyAttributesAndData(
215 SecKeychainItemRef itemRef
,
216 const SecKeychainAttributeList
* attrList
,
218 const void* data
) const {
221 return errSecAuthFailed
;
222 const char* fail_trigger
= "fail_me";
223 if (length
== strlen(fail_trigger
) &&
224 memcmp(data
, fail_trigger
, length
) == 0) {
225 return errSecAuthFailed
;
228 MockKeychainItemType key
=
229 reinterpret_cast<MockKeychainItemType
>(itemRef
) - 1;
230 if (keychain_attr_list_
.find(key
) == keychain_attr_list_
.end())
231 return errSecInvalidItemRef
;
233 MockAppleKeychain
* mutable_this
= const_cast<MockAppleKeychain
*>(this);
235 for (UInt32 change_attr
= 0; change_attr
< attrList
->count
; ++change_attr
) {
236 if (attrList
->attr
[change_attr
].tag
== kSecCreatorItemAttr
) {
237 void* data
= attrList
->attr
[change_attr
].data
;
238 mutable_this
->SetTestDataCreator(key
, *(static_cast<OSType
*>(data
)));
245 mutable_this
->SetTestDataPasswordBytes(key
, data
, length
);
249 OSStatus
MockAppleKeychain::ItemFreeAttributesAndData(
250 SecKeychainAttributeList
* attrList
,
252 --attribute_data_copy_count_
;
256 OSStatus
MockAppleKeychain::ItemDelete(SecKeychainItemRef itemRef
) const {
258 return errSecAuthFailed
;
259 MockKeychainItemType key
=
260 reinterpret_cast<MockKeychainItemType
>(itemRef
) - 1;
262 for (unsigned int i
= 0; i
< keychain_attr_list_
[key
].count
; ++i
) {
263 if (keychain_attr_list_
[key
].attr
[i
].data
)
264 free(keychain_attr_list_
[key
].attr
[i
].data
);
266 free(keychain_attr_list_
[key
].attr
);
267 if (keychain_data_
[key
].data
)
268 free(keychain_data_
[key
].data
);
270 keychain_attr_list_
.erase(key
);
271 keychain_data_
.erase(key
);
272 added_via_api_
.erase(key
);
276 OSStatus
MockAppleKeychain::SearchCreateFromAttributes(
277 CFTypeRef keychainOrArray
,
278 SecItemClass itemClass
,
279 const SecKeychainAttributeList
* attrList
,
280 SecKeychainSearchRef
* searchRef
) const {
281 // Figure out which of our mock items matches, and set up the array we'll use
282 // to generate results out of SearchCopyNext.
283 remaining_search_results_
.clear();
284 for (MockKeychainAttributesMap::const_iterator it
=
285 keychain_attr_list_
.begin();
286 it
!= keychain_attr_list_
.end();
288 bool mock_item_matches
= true;
289 for (UInt32 search_attr
= 0; search_attr
< attrList
->count
; ++search_attr
) {
290 SecKeychainAttribute
* mock_attribute
=
291 AttributeWithTag(it
->second
, attrList
->attr
[search_attr
].tag
);
292 if (mock_attribute
->length
!= attrList
->attr
[search_attr
].length
||
293 memcmp(mock_attribute
->data
, attrList
->attr
[search_attr
].data
,
294 attrList
->attr
[search_attr
].length
) != 0) {
295 mock_item_matches
= false;
299 if (mock_item_matches
)
300 remaining_search_results_
.push_back(it
->first
);
304 *searchRef
= kDummySearchRef
;
305 ++search_copy_count_
;
309 bool MockAppleKeychain::AlreadyContainsInternetPassword(
310 UInt32 serverNameLength
,
311 const char* serverName
,
312 UInt32 securityDomainLength
,
313 const char* securityDomain
,
314 UInt32 accountNameLength
,
315 const char* accountName
,
319 SecProtocolType protocol
,
320 SecAuthenticationType authenticationType
) const {
321 for (MockKeychainAttributesMap::const_iterator it
=
322 keychain_attr_list_
.begin();
323 it
!= keychain_attr_list_
.end();
325 SecKeychainAttribute
* attribute
;
326 attribute
= AttributeWithTag(it
->second
, kSecServerItemAttr
);
327 if ((attribute
->length
!= serverNameLength
) ||
328 (attribute
->data
== NULL
&& *serverName
!= '\0') ||
329 (attribute
->data
!= NULL
&& *serverName
== '\0') ||
331 (const char*) attribute
->data
,
332 serverNameLength
) != 0) {
335 attribute
= AttributeWithTag(it
->second
, kSecSecurityDomainItemAttr
);
336 if ((attribute
->length
!= securityDomainLength
) ||
337 (attribute
->data
== NULL
&& *securityDomain
!= '\0') ||
338 (attribute
->data
!= NULL
&& *securityDomain
== '\0') ||
339 strncmp(securityDomain
,
340 (const char*) attribute
->data
,
341 securityDomainLength
) != 0) {
344 attribute
= AttributeWithTag(it
->second
, kSecAccountItemAttr
);
345 if ((attribute
->length
!= accountNameLength
) ||
346 (attribute
->data
== NULL
&& *accountName
!= '\0') ||
347 (attribute
->data
!= NULL
&& *accountName
== '\0') ||
349 (const char*) attribute
->data
,
350 accountNameLength
) != 0) {
353 attribute
= AttributeWithTag(it
->second
, kSecPathItemAttr
);
354 if ((attribute
->length
!= pathLength
) ||
355 (attribute
->data
== NULL
&& *path
!= '\0') ||
356 (attribute
->data
!= NULL
&& *path
== '\0') ||
358 (const char*) attribute
->data
,
362 attribute
= AttributeWithTag(it
->second
, kSecPortItemAttr
);
363 if ((attribute
->data
== NULL
) ||
364 (port
!= *(static_cast<UInt32
*>(attribute
->data
)))) {
367 attribute
= AttributeWithTag(it
->second
, kSecProtocolItemAttr
);
368 if ((attribute
->data
== NULL
) ||
369 (protocol
!= *(static_cast<SecProtocolType
*>(attribute
->data
)))) {
372 attribute
= AttributeWithTag(it
->second
, kSecAuthenticationTypeItemAttr
);
373 if ((attribute
->data
== NULL
) ||
374 (authenticationType
!=
375 *(static_cast<SecAuthenticationType
*>(attribute
->data
)))) {
378 // The keychain already has this item, since all fields other than the
385 OSStatus
MockAppleKeychain::AddInternetPassword(
386 SecKeychainRef keychain
,
387 UInt32 serverNameLength
,
388 const char* serverName
,
389 UInt32 securityDomainLength
,
390 const char* securityDomain
,
391 UInt32 accountNameLength
,
392 const char* accountName
,
396 SecProtocolType protocol
,
397 SecAuthenticationType authenticationType
,
398 UInt32 passwordLength
,
399 const void* passwordData
,
400 SecKeychainItemRef
* itemRef
) const {
402 return errSecAuthFailed
;
404 // Check for the magic duplicate item trigger.
405 if (strcmp(serverName
, "some.domain.com") == 0)
406 return errSecDuplicateItem
;
408 // If the account already exists in the keychain, we don't add it.
409 if (AlreadyContainsInternetPassword(serverNameLength
, serverName
,
410 securityDomainLength
, securityDomain
,
411 accountNameLength
, accountName
,
414 authenticationType
)) {
415 return errSecDuplicateItem
;
418 // Pick the next unused slot.
419 MockKeychainItemType key
= next_item_key_
++;
421 // Initialize keychain data storage at the target location.
422 InitializeKeychainData(key
);
424 MockAppleKeychain
* mutable_this
= const_cast<MockAppleKeychain
*>(this);
425 mutable_this
->SetTestDataBytes(key
, kSecServerItemAttr
, serverName
,
427 mutable_this
->SetTestDataBytes(key
, kSecSecurityDomainItemAttr
,
428 securityDomain
, securityDomainLength
);
429 mutable_this
->SetTestDataBytes(key
, kSecAccountItemAttr
, accountName
,
431 mutable_this
->SetTestDataBytes(key
, kSecPathItemAttr
, path
, pathLength
);
432 mutable_this
->SetTestDataPort(key
, port
);
433 mutable_this
->SetTestDataProtocol(key
, protocol
);
434 mutable_this
->SetTestDataAuthType(key
, authenticationType
);
435 mutable_this
->SetTestDataPasswordBytes(key
, passwordData
,
437 base::Time::Exploded exploded_time
;
438 base::Time::Now().UTCExplode(&exploded_time
);
439 char time_string
[128];
440 snprintf(time_string
, sizeof(time_string
), "%04d%02d%02d%02d%02d%02dZ",
441 exploded_time
.year
, exploded_time
.month
, exploded_time
.day_of_month
,
442 exploded_time
.hour
, exploded_time
.minute
, exploded_time
.second
);
443 mutable_this
->SetTestDataString(key
, kSecCreationDateItemAttr
, time_string
);
445 added_via_api_
.insert(key
);
448 *itemRef
= reinterpret_cast<SecKeychainItemRef
>(key
+ 1);
449 ++keychain_item_copy_count_
;
454 OSStatus
MockAppleKeychain::SearchCopyNext(SecKeychainSearchRef searchRef
,
455 SecKeychainItemRef
* itemRef
) const {
456 if (remaining_search_results_
.empty())
457 return errSecItemNotFound
;
458 MockKeychainItemType key
= remaining_search_results_
.front();
459 remaining_search_results_
.erase(remaining_search_results_
.begin());
460 *itemRef
= reinterpret_cast<SecKeychainItemRef
>(key
+ 1);
461 ++keychain_item_copy_count_
;
465 void MockAppleKeychain::Free(CFTypeRef ref
) const {
469 if (ref
== kDummySearchRef
) {
470 --search_copy_count_
;
472 --keychain_item_copy_count_
;
476 int MockAppleKeychain::UnfreedSearchCount() const {
477 return search_copy_count_
;
480 int MockAppleKeychain::UnfreedKeychainItemCount() const {
481 return keychain_item_copy_count_
;
484 int MockAppleKeychain::UnfreedAttributeDataCount() const {
485 return attribute_data_copy_count_
;
488 bool MockAppleKeychain::CreatorCodesSetForAddedItems() const {
489 for (std::set
<MockKeychainItemType
>::const_iterator
490 i
= added_via_api_
.begin();
491 i
!= added_via_api_
.end();
493 SecKeychainAttribute
* attribute
= AttributeWithTag(keychain_attr_list_
[*i
],
494 kSecCreatorItemAttr
);
495 OSType
* data
= static_cast<OSType
*>(attribute
->data
);
502 void MockAppleKeychain::AddTestItem(const KeychainTestData
& item_data
) {
503 MockKeychainItemType key
= next_item_key_
++;
505 InitializeKeychainData(key
);
506 SetTestDataAuthType(key
, item_data
.auth_type
);
507 SetTestDataString(key
, kSecServerItemAttr
, item_data
.server
);
508 SetTestDataProtocol(key
, item_data
.protocol
);
509 SetTestDataString(key
, kSecPathItemAttr
, item_data
.path
);
510 SetTestDataPort(key
, item_data
.port
);
511 SetTestDataString(key
, kSecSecurityDomainItemAttr
,
512 item_data
.security_domain
);
513 SetTestDataString(key
, kSecCreationDateItemAttr
, item_data
.creation_date
);
514 SetTestDataString(key
, kSecAccountItemAttr
, item_data
.username
);
515 SetTestDataPasswordString(key
, item_data
.password
);
516 SetTestDataNegativeItem(key
, item_data
.negative_item
);
519 } // namespace crypto