2 * Copyright (c) 2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 #include "kc-30-xara-helpers.h"
26 #ifndef kc_30_xara_item_helpers_h
27 #define kc_30_xara_item_helpers_h
31 #pragma clang diagnostic push
32 #pragma clang diagnostic ignored "-Wunused-variable"
33 #pragma clang diagnostic ignored "-Wunused-function"
36 static CFMutableDictionaryRef
makeBaseItemDictionary(CFStringRef itemclass
) {
37 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
38 CFDictionaryAddValue(query
, kSecReturnRef
, kCFBooleanTrue
);
39 CFDictionarySetValue(query
, kSecClass
, itemclass
);
41 if(CFEqual(itemclass
, kSecClassInternetPassword
)) {
42 CFDictionarySetValue(query
, kSecAttrServer
, CFSTR("test_service"));
43 CFDictionarySetValue(query
, kSecAttrAuthenticationType
, CFSTR("dflt")); // Default, I guess?
45 // Generic passwords have services
46 CFDictionarySetValue(query
, kSecAttrService
, CFSTR("test_service"));
51 static CFMutableDictionaryRef
makeQueryItemDictionary(SecKeychainRef kc
, CFStringRef itemclass
) {
52 CFMutableDictionaryRef query
= makeBaseItemDictionary(itemclass
);
54 CFMutableArrayRef searchList
= (CFMutableArrayRef
) CFArrayCreateMutable(kCFAllocatorDefault
, 1, &kCFTypeArrayCallBacks
);
55 CFArrayAppendValue((CFMutableArrayRef
)searchList
, kc
);
56 CFDictionarySetValue(query
, kSecMatchSearchList
, searchList
);
58 CFDictionarySetValue(query
, kSecMatchLimit
, kSecMatchLimitAll
);
63 static CFMutableDictionaryRef
makeQueryCustomItemDictionary(SecKeychainRef kc
, CFStringRef itemclass
, CFStringRef label
) {
64 CFMutableDictionaryRef query
= makeQueryItemDictionary(kc
, itemclass
);
65 CFDictionarySetValue(query
, kSecAttrLabel
, label
);
69 static CFMutableDictionaryRef
makeAddCustomItemDictionary(SecKeychainRef kc
, CFStringRef itemclass
, CFStringRef label
, CFStringRef account
) {
70 CFMutableDictionaryRef query
= makeBaseItemDictionary(itemclass
);
72 CFDictionaryAddValue(query
, kSecUseKeychain
, kc
);
73 CFDictionarySetValue(query
, kSecAttrAccount
, account
);
74 CFDictionarySetValue(query
, kSecAttrComment
, CFSTR("a comment"));
75 CFDictionarySetValue(query
, kSecAttrLabel
, label
);
76 CFDictionarySetValue(query
, kSecValueData
, CFDataCreate(NULL
, (void*)"data", 4));
80 static CFMutableDictionaryRef
makeAddItemDictionary(SecKeychainRef kc
, CFStringRef itemclass
, CFStringRef label
) {
81 return makeAddCustomItemDictionary(kc
, itemclass
, label
, CFSTR("test_account"));
84 static SecKeychainItemRef
makeCustomItem(const char* name
, SecKeychainRef kc
, CFDictionaryRef addDictionary
) {
85 CFTypeRef result
= NULL
;
86 ok_status(SecItemAdd(addDictionary
, &result
), "%s: SecItemAdd", name
);
87 ok(result
!= NULL
, "%s: SecItemAdd returned a result", name
);
89 SecKeychainItemRef item
= (SecKeychainItemRef
) result
;
90 ok(item
!= NULL
, "%s: Couldn't convert into SecKeychainItemRef", name
);
94 #define makeCustomItemTests 3
96 static SecKeychainItemRef
makeItem(const char* name
, SecKeychainRef kc
, CFStringRef itemclass
, CFStringRef label
) {
97 CFMutableDictionaryRef query
= makeAddItemDictionary(kc
, itemclass
, label
);
99 SecKeychainItemRef item
= makeCustomItem(name
, kc
, query
);
101 CFReleaseNull(query
);
104 #define makeItemTests makeCustomItemTests
106 static void makeCustomDuplicateItem(const char* name
, SecKeychainRef kc
, CFStringRef itemclass
, CFStringRef label
) {
107 CFMutableDictionaryRef query
= makeAddItemDictionary(kc
, itemclass
, label
);
109 CFTypeRef result
= NULL
;
110 is(SecItemAdd(query
, &result
), errSecDuplicateItem
, "%s: SecItemAdd (duplicate)", name
);
112 CFReleaseNull(query
);
114 #define makeCustomDuplicateItemTests 1
116 static void makeDuplicateItem(const char* name
, SecKeychainRef kc
, CFStringRef itemclass
) {
117 return makeCustomDuplicateItem(name
, kc
, itemclass
, CFSTR("test_label"));
119 #define makeDuplicateItemTests makeCustomDuplicateItemTests
121 static void makeCustomItemWithIntegrity(const char* name
, SecKeychainRef kc
, CFStringRef itemclass
, CFStringRef label
, CFStringRef expectedHash
) {
122 SecKeychainItemRef item
= makeItem(name
, kc
, itemclass
, label
);
123 checkIntegrityHash(name
, item
, expectedHash
);
126 #define makeCustomItemWithIntegrityTests (makeItemTests + checkIntegrityHashTests)
128 static void makeItemWithIntegrity(const char* name
, SecKeychainRef kc
, CFStringRef itemclass
, CFStringRef expectedHash
) {
129 makeCustomItemWithIntegrity(name
, kc
, itemclass
, CFSTR("test_label"), expectedHash
);
131 #define makeItemWithIntegrityTests (makeCustomItemWithIntegrityTests)
133 static void testAddItem(CFStringRef itemclass
, CFStringRef expectedHash
) {
135 sprintf(name
, "testAddItem[%s]", CFStringGetCStringPtr(itemclass
, kCFStringEncodingUTF8
));
136 secdebugfunc("integrity", "************************************* %s", name
);
138 SecKeychainRef kc
= newKeychain(name
);
139 makeItemWithIntegrity(name
, kc
, itemclass
, expectedHash
);
141 ok_status(SecKeychainDelete(kc
), "%s: SecKeychainDelete", name
);
143 #define testAddItemTests (newKeychainTests + makeItemWithIntegrityTests + 1)
145 static void testCopyMatchingItem(CFStringRef itemclass
, CFStringRef expectedHash
) {
147 sprintf(name
, "testCopyMatchingItem[%s]", CFStringGetCStringPtr(itemclass
, kCFStringEncodingUTF8
));
148 secdebugfunc("integrity", "************************************* %s", name
);
150 SecKeychainRef kc
= newKeychain(name
);
151 makeItemWithIntegrity(name
, kc
, itemclass
, expectedHash
);
153 SecKeychainItemRef item
= checkN(name
, makeQueryItemDictionary(kc
, itemclass
), 1);
154 checkIntegrityHash(name
, item
, expectedHash
);
155 ok_status(SecKeychainDelete(kc
), "%s: SecKeychainDelete", name
);
157 #define testCopyMatchingItemTests (newKeychainTests + makeItemWithIntegrityTests + checkNTests + checkIntegrityHashTests + 1)
159 static void testUpdateItem(CFStringRef itemclass
, CFStringRef expectedHashOrig
, CFStringRef expectedHashAfter
) {
161 sprintf(name
, "testUpdateItem[%s]", CFStringGetCStringPtr(itemclass
, kCFStringEncodingUTF8
));
162 secdebugfunc("integrity", "************************************* %s", name
);
164 SecKeychainRef kc
= newKeychain(name
);
165 makeItemWithIntegrity(name
, kc
, itemclass
, expectedHashOrig
);
167 CFMutableDictionaryRef query
= makeQueryItemDictionary(kc
, itemclass
);
168 CFMutableDictionaryRef update
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
169 CFDictionarySetValue(update
, kSecAttrComment
, CFSTR("a modification"));
170 CFDictionarySetValue(update
, kSecAttrAccount
, CFSTR("a account modification"));
171 CFDictionarySetValue(update
, kSecAttrLabel
, CFSTR("a label modification"));
172 ok_status(SecItemUpdate(query
, update
), "%s: SecItemUpdate", name
);
174 CFReleaseNull(update
);
176 SecKeychainItemRef item
= checkN(name
, makeQueryItemDictionary(kc
, itemclass
), 1);
177 checkIntegrityHash(name
, item
, expectedHashAfter
);
180 // Check that updating data works
181 update
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
182 CFDictionarySetValue(update
, kSecValueData
, CFDataCreate(NULL
, (void*)"data", 4));
183 ok_status(SecItemUpdate(query
, update
), "%s: SecItemUpdate", name
);
185 item
= checkN(name
, makeQueryItemDictionary(kc
, itemclass
), 1);
186 checkIntegrityHash(name
, item
, expectedHashAfter
);
188 CFReleaseNull(query
);
189 CFReleaseNull(update
);
191 ok_status(SecKeychainDelete(kc
), "%s: SecKeychainDelete", name
);
193 #define testUpdateItemTests (newKeychainTests + makeItemWithIntegrityTests \
194 + 1 + checkNTests + checkIntegrityHashTests \
195 + 1 + checkNTests + checkIntegrityHashTests \
198 static void testAddDuplicateItem(CFStringRef itemclass
, CFStringRef expectedHash
) {
200 sprintf(name
, "testAddDuplicateItem[%s]", CFStringGetCStringPtr(itemclass
, kCFStringEncodingUTF8
));
201 secdebugfunc("integrity", "************************************* %s", name
);
203 SecKeychainRef kc
= newKeychain(name
);
204 makeItemWithIntegrity(name
, kc
, itemclass
, expectedHash
);
206 makeDuplicateItem(name
, kc
, itemclass
);
208 ok_status(SecKeychainDelete(kc
), "%s: SecKeychainDelete", name
);
210 #define testAddDuplicateItemTests (newKeychainTests + makeItemWithIntegrityTests + makeDuplicateItemTests + 1)
212 static void testDeleteItem(CFStringRef itemclass
, CFStringRef expectedHash
) {
214 sprintf(name
, "testDeleteItem[%s]", CFStringGetCStringPtr(itemclass
, kCFStringEncodingUTF8
));
215 secdebugfunc("integrity", "************************************* %s", name
);
217 SecKeychainRef kc
= newKeychain(name
);
218 makeItemWithIntegrity(name
, kc
, itemclass
, expectedHash
);
220 SecKeychainItemRef item
= checkN(name
, makeQueryItemDictionary(kc
, itemclass
), 1);
221 checkIntegrityHash(name
, item
, expectedHash
);
223 ok_status(SecKeychainItemDelete(item
), "%s: SecKeychainItemDelete", name
);
224 checkN(name
, makeQueryItemDictionary(kc
, itemclass
), 0);
225 ok_status(SecKeychainDelete(kc
), "%s: SecKeychainDelete", name
);
227 #define testDeleteItemTests (newKeychainTests + makeItemWithIntegrityTests + checkNTests + checkIntegrityHashTests + 1 + checkNTests + 1)
229 static void writeEmptyV512Keychain(const char* name
, const char* keychainFile
);
231 // This test is to find <rdar://problem/23515265> CrashTracer: accountsd at …curity: Security::KeychainCore::CCallbackMgr::consume + 387
233 // The issue was that items could remain in the Keychain cache, even after the
234 // actual object was freed. The main path involved updating an item so that it
235 // had the same primary key as an item which was in the cache but not in the
236 // database (this could be caused by another process deleting the item and us
237 // not receiving the notification).
239 // This test should pass. Failure is shown by crashing.
241 static void testUpdateRetainedItem(CFStringRef itemclass
) {
243 sprintf(name
, "testUpdateRetainedItem[%s]", CFStringGetCStringPtr(itemclass
, kCFStringEncodingUTF8
));
244 secdebugfunc("integrity", "************************************* %s", name
);
246 writeEmptyV512Keychain(name
, keychainFile
);
247 SecKeychainRef kc
= openCustomKeychain(name
, "test.keychain", "password");
249 SecKeychainItemRef item
= makeCustomItem(name
, kc
, makeAddCustomItemDictionary(kc
, itemclass
, CFSTR("test_label"), CFSTR("account1")));
251 CFRelease(checkN(name
, makeQueryCustomItemDictionary(kc
, itemclass
, CFSTR("test_label")), 1));
253 is(CFGetRetainCount(item
), 1, "%s: CFGetRetainCount(item)", name
);
255 // Bump our local database version number a few times, so we'll re-read the database when we reset it later
256 CFReleaseSafe(makeCustomItem(name
, kc
, makeAddCustomItemDictionary(kc
, itemclass
, CFSTR("version"), CFSTR("version"))));
257 CFReleaseSafe(makeCustomItem(name
, kc
, makeAddCustomItemDictionary(kc
, itemclass
, CFSTR("bump"), CFSTR("bump"))));
259 // Simulate another process deleting the items we just made, and us not receiving the notification
260 writeEmptyV512Keychain(name
, keychainFile
);
262 // Generate some keychain notifications on a different keychain so the AppleDatabase will reload test.keychain
263 SecKeychainRef kc2
= newCustomKeychain(name
, "unrelated.keychain", "password");
264 CFReleaseSafe(makeCustomItem(name
, kc2
, makeAddCustomItemDictionary(kc
, itemclass
, CFSTR("unrelated1_label"), CFSTR("unrelated1"))));
265 ok_status(SecKeychainDelete(kc2
), "%s: SecKeychainDelete", name
);
267 secdebugfunc("integrity", "************************************* should reload database\n");
269 SecKeychainItemRef item2
= makeCustomItem(name
, kc
, makeAddCustomItemDictionary(kc
, itemclass
, CFSTR("not_a_test_label"), CFSTR("account2")));
270 CFReleaseSafe(checkN(name
, makeQueryCustomItemDictionary(kc
, itemclass
, CFSTR("not_a_test_label")), 1));
271 is(CFGetRetainCount(item2
), 1, "%s: CFGetRetainCount(item2)", name
);
273 // Now, update the second item so it would collide with the first
274 CFMutableDictionaryRef query
= makeQueryCustomItemDictionary(kc
, itemclass
, CFSTR("not_a_test_label"));
275 CFMutableDictionaryRef update
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
276 CFDictionarySetValue(update
, kSecAttrAccount
, CFSTR("account1"));
277 CFDictionarySetValue(update
, kSecAttrLabel
, CFSTR("test_label"));
278 ok_status(SecItemUpdate(query
, update
), "%s: SecItemUpdate", name
);
280 is(CFGetRetainCount(item
), 1, "%s: CFGetRetainCount(item)", name
);
283 SecKeychainItemRef result
= checkN(name
, makeQueryCustomItemDictionary(kc
, itemclass
, CFSTR("test_label")), 1);
284 CFReleaseNull(result
);
285 ok_status(SecKeychainDelete(kc
), "%s: SecKeychainDelete", name
);
287 #define testUpdateRetainedItemTests (openCustomKeychainTests + makeCustomItemTests + checkNTests \
288 + 1 + makeCustomItemTests + makeCustomItemTests \
289 + newCustomKeychainTests + makeCustomItemTests + 1 \
290 + makeCustomItemTests + checkNTests + 1 \
291 + 1 + 1 + checkNTests + 1)
296 #endif /* TARGET_OS_MAC */
299 #endif /* kc_30_xara_item_helpers_h */