2 * Copyright (c) 2010,2012-2014 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@
25 #include <TargetConditionals.h>
27 #if TARGET_IPHONE_SIMULATOR
28 #define USE_KEYSTORE 0
29 #else /* No AppleKeyStore.kext on this OS. */
30 #define USE_KEYSTORE 1
34 #include <CoreFoundation/CoreFoundation.h>
35 #include <Security/SecBase.h>
36 #include <Security/SecItem.h>
37 #include <Security/SecInternal.h>
38 #include <Security/SecItemPriv.h>
39 #include <utilities/array_size.h>
42 #include <AssertMacros.h>
52 #include "../Security_regressions.h"
54 struct test_persistent_s
{
56 CFDictionaryRef query
;
57 CFDictionaryRef query1
;
58 CFDictionaryRef query2
;
59 CFMutableDictionaryRef query3
;
60 CFMutableDictionaryRef query4
;
63 static void test_persistent(struct test_persistent_s
*p
)
66 CFNumberRef eighty
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &v_eighty
);
67 const char *v_data
= "test";
68 CFDataRef pwdata
= CFDataCreate(NULL
, (UInt8
*)v_data
, strlen(v_data
));
69 const void *keys
[] = {
75 kSecAttrAuthenticationType
,
76 kSecReturnPersistentRef
,
79 const void *values
[] = {
80 kSecClassInternetPassword
,
89 CFDictionaryRef item
= CFDictionaryCreate(NULL
, keys
, values
,
90 array_size(keys
), &kCFTypeDictionaryKeyCallBacks
,
91 &kCFTypeDictionaryValueCallBacks
);
94 // NUKE anything we might have left around from a previous test run so we don't crash.
96 ok_status(SecItemAdd(item
, &p
->persist
[0]), "add internet password");
97 CFTypeRef results
= NULL
;
98 CFTypeRef results2
= NULL
;
100 skip("no persistent ref", 6, ok(p
->persist
[0], "got back persistent ref"));
102 /* Create a dict with all attrs except the data. */
103 keys
[(array_size(keys
)) - 2] = kSecReturnAttributes
;
104 p
->query
= CFDictionaryCreate(NULL
, keys
, values
,
105 (array_size(keys
)) - 1, &kCFTypeDictionaryKeyCallBacks
,
106 &kCFTypeDictionaryValueCallBacks
);
107 ok_status(SecItemCopyMatching(p
->query
, &results
), "find internet password by attr");
109 const void *keys_persist
[] = {
110 kSecReturnAttributes
,
111 kSecValuePersistentRef
113 const void *values_persist
[] = {
117 p
->query2
= CFDictionaryCreate(NULL
, keys_persist
, values_persist
,
118 (array_size(keys_persist
)), &kCFTypeDictionaryKeyCallBacks
,
119 &kCFTypeDictionaryValueCallBacks
);
120 ok_status(SecItemCopyMatching(p
->query2
, &results2
), "find internet password by persistent ref");
121 ok(CFEqual(results
, results2
? results2
: CFSTR("")), "same item (attributes)");
123 CFReleaseNull(results
);
124 CFReleaseNull(results2
);
126 ok_status(SecItemDelete(p
->query
), "delete internet password");
128 ok_status(!SecItemCopyMatching(p
->query
, &results
),
129 "don't find internet password by attributes");
130 ok(!results
, "no results");
133 /* clean up left over from aborted run */
135 CFDictionaryRef cleanup
= CFDictionaryCreate(NULL
, (const void **)&kSecValuePersistentRef
,
136 &results
, 1, &kCFTypeDictionaryKeyCallBacks
,
137 &kCFTypeDictionaryValueCallBacks
);
138 SecItemDelete(cleanup
);
143 ok_status(!SecItemCopyMatching(p
->query2
, &results2
),
144 "don't find internet password by persistent ref anymore");
145 ok(!results2
, "no results");
147 CFReleaseNull(p
->persist
[0]);
149 /* Add a new item and get it's persitant ref. */
150 ok_status(SecItemAdd(item
, &p
->persist
[0]), "add internet password");
151 p
->persist
[1] = NULL
;
152 CFMutableDictionaryRef item2
= CFDictionaryCreateMutableCopy(NULL
, 0, item
);
153 CFDictionarySetValue(item2
, kSecAttrAccount
, CFSTR("johndoe-bu"));
154 // NUKE anything we might have left around from a previous test run so we don't crash.
155 SecItemDelete(item2
);
156 ok_status(SecItemAdd(item2
, &p
->persist
[1]), "add second internet password");
157 CFMutableDictionaryRef update
= NULL
;
158 CFStringRef server
= NULL
;
160 skip("no persistent ref", 3, ok(p
->persist
[0], "got back persistent ref from first internet password"));
162 is(CFGetTypeID(p
->persist
[0]), CFDataGetTypeID(), "result is a CFData");
163 p
->query3
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
,
164 &kCFTypeDictionaryValueCallBacks
);
165 CFDictionaryAddValue(p
->query3
, kSecValuePersistentRef
, p
->persist
[0]);
166 update
= CFDictionaryCreateMutable(NULL
, 0, NULL
, NULL
);
167 CFDictionaryAddValue(update
, kSecAttrServer
, CFSTR("zuigt.com"));
168 ok_status(SecItemUpdate(p
->query3
, update
), "update via persitant ref");
170 /* Verify that the update really worked. */
171 CFDictionaryAddValue(p
->query3
, kSecReturnAttributes
, kCFBooleanTrue
);
172 ok_status(SecItemCopyMatching(p
->query3
, &results2
), "find updated internet password by persistent ref");
173 server
= CFDictionaryGetValue(results2
, kSecAttrServer
);
174 ok(CFEqual(server
, CFSTR("zuigt.com")), "verify attribute was modified by update");
175 CFReleaseNull(results2
);
176 CFDictionaryRemoveValue(p
->query3
, kSecReturnAttributes
);
180 skip("no persistent ref", 2, ok(p
->persist
[1], "got back persistent ref"));
182 /* Verify that item2 wasn't affected by the update. */
183 p
->query4
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
,
184 &kCFTypeDictionaryValueCallBacks
);
185 CFDictionaryAddValue(p
->query4
, kSecValuePersistentRef
, p
->persist
[1]);
186 CFDictionaryAddValue(p
->query4
, kSecReturnAttributes
, kCFBooleanTrue
);
187 ok_status(SecItemCopyMatching(p
->query4
, &results2
), "find non updated internet password by persistent ref");
188 server
= CFDictionaryGetValue(results2
, kSecAttrServer
);
189 ok(CFEqual(server
, CFSTR("zuigt.nl")), "verify second items attribute was not modified by update");
190 CFReleaseNull(results2
);
193 /* Delete the item via persitant ref. */
194 ok_status(SecItemDelete(p
->query3
), "delete via persitant ref");
195 is_status(SecItemCopyMatching(p
->query3
, &results2
), errSecItemNotFound
,
196 "don't find deleted internet password by persistent ref");
197 CFReleaseNull(results2
);
198 ok_status(SecItemCopyMatching(p
->query4
, &results2
),
199 "find non deleted internet password by persistent ref");
200 CFReleaseNull(results2
);
202 CFReleaseNull(update
);
204 CFReleaseNull(item2
);
205 CFReleaseNull(eighty
);
206 CFReleaseNull(pwdata
);
209 static void test_persistent2(struct test_persistent_s
*p
)
211 CFTypeRef results
= NULL
;
212 CFTypeRef results2
= NULL
;
214 ok_status(!SecItemCopyMatching(p
->query
, &results
),
215 "don't find internet password by attributes");
216 ok(!results
, "no results");
218 ok_status(!SecItemCopyMatching(p
->query2
, &results2
),
219 "don't find internet password by persistent ref anymore");
220 ok(!results2
, "no results");
223 ok_status(SecItemCopyMatching(p
->query4
, &results2
), "find non updated internet password by persistent ref");
224 skip("non updated internet password by persistent ref NOT FOUND!", 2, results2
);
225 ok(results2
, "non updated internet password not found");
226 CFStringRef server
= CFDictionaryGetValue(results2
, kSecAttrServer
);
227 ok(CFEqual(server
, CFSTR("zuigt.nl")), "verify second items attribute was not modified by update");
228 CFReleaseNull(results2
);
231 is_status(SecItemCopyMatching(p
->query3
, &results2
), errSecItemNotFound
,
232 "don't find deleted internet password by persistent ref");
233 CFReleaseNull(results2
);
234 ok_status(SecItemCopyMatching(p
->query4
, &results2
),
235 "find non deleted internet password by persistent ref");
236 CFReleaseNull(results2
);
238 ok_status(SecItemDelete(p
->query4
),"Deleted internet password by persistent ref");
241 CFRelease(p
->query2
);
242 CFRelease(p
->query3
);
243 CFRelease(p
->query4
);
244 CFReleaseNull(p
->persist
[0]);
245 CFReleaseNull(p
->persist
[1]);
248 static CFMutableDictionaryRef
test_create_lockdown_identity_query(void) {
249 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 0, NULL
, NULL
);
250 CFDictionaryAddValue(query
, kSecClass
, kSecClassGenericPassword
);
251 CFDictionaryAddValue(query
, kSecAttrAccount
, CFSTR("test-delete-me"));
252 CFDictionaryAddValue(query
, kSecAttrAccessGroup
, CFSTR("lockdown-identities"));
256 static CFMutableDictionaryRef
test_create_managedconfiguration_query(void) {
257 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 0, NULL
, NULL
);
258 CFDictionaryAddValue(query
, kSecClass
, kSecClassGenericPassword
);
259 CFDictionaryAddValue(query
, kSecAttrService
, CFSTR("com.apple.managedconfiguration"));
260 CFDictionaryAddValue(query
, kSecAttrAccount
, CFSTR("Public"));
261 CFDictionaryAddValue(query
, kSecAttrAccessGroup
, CFSTR("apple"));
265 static void test_add_lockdown_identity_items(void) {
266 CFMutableDictionaryRef query
= test_create_lockdown_identity_query();
267 const char *v_data
= "lockdown identity data (which should be a cert + key)";
268 CFDataRef pwdata
= CFDataCreate(NULL
, (UInt8
*)v_data
, strlen(v_data
));
269 CFDictionaryAddValue(query
, kSecValueData
, pwdata
);
270 ok_status(SecItemAdd(query
, NULL
), "test_add_lockdown_identity_items");
271 CFReleaseSafe(pwdata
);
272 CFReleaseSafe(query
);
275 static void test_remove_lockdown_identity_items(void) {
276 CFMutableDictionaryRef query
= test_create_lockdown_identity_query();
277 ok_status(SecItemDelete(query
), "test_remove_lockdown_identity_items");
278 CFReleaseSafe(query
);
281 static void test_no_find_lockdown_identity_item(void) {
282 CFMutableDictionaryRef query
= test_create_lockdown_identity_query();
283 is_status(SecItemCopyMatching(query
, NULL
), errSecItemNotFound
,
284 "test_no_find_lockdown_identity_item");
285 CFReleaseSafe(query
);
288 static CFMutableDictionaryRef
test_create_sysbound_query(void) {
289 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 0, NULL
, NULL
);
290 CFDictionaryAddValue(query
, kSecClass
, kSecClassGenericPassword
);
291 CFDictionaryAddValue(query
, kSecAttrAccount
, CFSTR("sysbound"));
292 CFDictionaryAddValue(query
, kSecAttrAccessGroup
, CFSTR("apple"));
296 static void test_add_sysbound_item(void) {
297 CFMutableDictionaryRef query
= test_create_sysbound_query();
298 int32_t val
= kSecSecAttrSysBoundPreserveDuringRestore
;
299 CFNumberRef num
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &val
);
300 CFDictionaryAddValue(query
, kSecAttrSysBound
, num
);
303 const char *v_data
= "sysbound identity data";
304 CFDataRef pwdata
= CFDataCreate(NULL
, (UInt8
*)v_data
, strlen(v_data
));
305 CFDictionaryAddValue(query
, kSecValueData
, pwdata
);
306 ok_status(SecItemAdd(query
, NULL
), "test_add_sysbound_item");
307 CFReleaseSafe(pwdata
);
308 CFReleaseSafe(query
);
311 static void test_remove_sysbound_item(void) {
312 CFMutableDictionaryRef query
= test_create_sysbound_query();
313 ok_status(SecItemDelete(query
), "test_remove_sysbound_item");
314 CFReleaseSafe(query
);
317 static void test_find_sysbound_item(OSStatus expectedCode
) {
318 CFMutableDictionaryRef query
= test_create_sysbound_query();
319 is_status(SecItemCopyMatching(query
, NULL
), expectedCode
,
320 "test_find_sysbound_item");
321 CFReleaseSafe(query
);
325 static void test_add_managedconfiguration_item(void) {
326 CFMutableDictionaryRef query
= test_create_managedconfiguration_query();
327 const char *v_data
= "public managedconfiguration password history data";
328 CFDataRef pwdata
= CFDataCreate(NULL
, (UInt8
*)v_data
, strlen(v_data
));
329 CFDictionaryAddValue(query
, kSecValueData
, pwdata
);
330 ok_status(SecItemAdd(query
, NULL
), "test_add_managedconfiguration_item");
331 CFReleaseSafe(pwdata
);
332 CFReleaseSafe(query
);
335 static void test_find_managedconfiguration_item(void) {
336 CFMutableDictionaryRef query
= test_create_managedconfiguration_query();
337 ok_status(SecItemCopyMatching(query
, NULL
), "test_find_managedconfiguration_item");
338 ok_status(SecItemDelete(query
), "test_find_managedconfiguration_item (deleted)");
339 CFReleaseSafe(query
);
343 #define DATA_ARG(x) (x) ? CFDataGetBytePtr((x)) : NULL, (x) ? (int)CFDataGetLength((x)) : 0
344 static CFDataRef
create_keybag(keybag_handle_t bag_type
, CFDataRef password
)
346 CFDataRef result
= NULL
;
350 keybag_handle_t handle
= bad_keybag_handle
;
351 require_noerr(aks_create_bag(DATA_ARG(password
), bag_type
, &handle
), out
);
352 require_noerr(aks_save_bag(handle
, &bag
, &bagLen
), out
);
354 result
= CFDataCreate(kCFAllocatorDefault
, bag
, bagLen
);
360 /* Test low level keychain migration from device to device interface. */
361 static void tests(void)
364 CFMutableDictionaryRef lock_down_query
= test_create_lockdown_identity_query();
365 (void)SecItemDelete(lock_down_query
);
366 CFReleaseNull(lock_down_query
);
370 CFNumberRef eighty
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &v_eighty
);
371 const char *v_data
= "test";
372 CFDataRef pwdata
= CFDataCreate(NULL
, (UInt8
*)v_data
, strlen(v_data
));
373 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 0, NULL
, NULL
);
374 CFDictionaryAddValue(query
, kSecClass
, kSecClassInternetPassword
);
375 CFDictionaryAddValue(query
, kSecAttrServer
, CFSTR("members.spamcop.net"));
376 CFDictionaryAddValue(query
, kSecAttrAccount
, CFSTR("smith"));
377 CFDictionaryAddValue(query
, kSecAttrPort
, eighty
);
378 CFDictionaryAddValue(query
, kSecAttrProtocol
, kSecAttrProtocolHTTP
);
379 CFDictionaryAddValue(query
, kSecAttrAuthenticationType
, kSecAttrAuthenticationTypeDefault
);
380 CFDictionaryAddValue(query
, kSecValueData
, pwdata
);
381 // NUKE anything we might have left around from a previous test run so we don't crash.
382 (void)SecItemDelete(query
);
384 ok_status(SecItemAdd(query
, NULL
), "add internet password");
385 is_status(SecItemAdd(query
, NULL
), errSecDuplicateItem
,
386 "add internet password again");
388 ok_status(SecItemCopyMatching(query
, NULL
), "Found the item we added");
390 struct test_persistent_s p
= {};
393 CFDataRef backup
= NULL
, keybag
= NULL
, password
= NULL
;
395 test_add_lockdown_identity_items();
396 test_add_sysbound_item();
399 keybag
= create_keybag(kAppleKeyStoreBackupBag
, password
);
401 keybag
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
404 ok(backup
= _SecKeychainCopyBackup(keybag
, password
),
405 "_SecKeychainCopyBackup");
407 test_add_managedconfiguration_item();
408 test_remove_lockdown_identity_items();
409 test_remove_sysbound_item();
411 ok_status(_SecKeychainRestoreBackup(backup
, keybag
, password
),
412 "_SecKeychainRestoreBackup");
413 CFReleaseSafe(backup
);
415 test_no_find_lockdown_identity_item();
416 test_find_sysbound_item(errSecItemNotFound
);
417 test_find_managedconfiguration_item();
419 ok_status(SecItemCopyMatching(query
, NULL
),
420 "Found the item we added after restore");
422 test_persistent2(&p
);
425 CFReleaseNull(keybag
);
426 keybag
= create_keybag(kAppleKeyStoreOTABackupBag
, password
);
429 ok(backup
= _SecKeychainCopyBackup(keybag
, password
),
430 "_SecKeychainCopyBackup");
431 ok_status(_SecKeychainRestoreBackup(backup
, keybag
, password
),
432 "_SecKeychainRestoreBackup");
433 ok_status(SecItemCopyMatching(query
, NULL
),
434 "Found the item we added after restore");
435 CFReleaseNull(backup
);
437 // force tombstone to be added, since it's not the default behavior per rdar://14680869
438 CFDictionaryAddValue(query
, kSecUseTombstones
, kCFBooleanTrue
);
440 ok_status(SecItemDelete(query
), "Deleted item we added");
443 CFReleaseNull(keybag
);
444 keybag
= create_keybag(kAppleKeyStoreOTABackupBag
/* use truthiness bag once it's there */, password
);
448 CFDictionaryAddValue(query
, kSecAttrSynchronizable
, kCFBooleanTrue
);
449 ok_status(SecItemAdd(query
, NULL
), "add internet password");
451 // and non-syncable item
452 test_add_managedconfiguration_item();
454 CFDictionaryRef syncableBackup
= NULL
;
456 CFErrorRef error
= NULL
;
457 CFDictionaryRef scratch
= NULL
;
459 skip("skipping syncable backup tests", 7,
460 ok_status(_SecKeychainBackupSyncable(keybag
, password
, NULL
, &syncableBackup
), "export items"));
462 // TODO: add item, call SecServerCopyTruthInTheCloud again
464 // CFShow(syncableBackup);
467 skip("skipping syncable backup tests", 6,
468 ok_status(SecItemCopyMatching(query
, NULL
), "find item we are about to destroy"));
470 skip("skipping syncable backup tests", 5,
471 ok_status(SecItemDelete(query
), "delete item we backed up"));
473 // ensure we added a tombstone
474 CFDictionaryAddValue(query
, kSecAttrTombstone
, kCFBooleanTrue
);
475 skip("skipping syncable backup tests", 4,
476 ok_status(SecItemCopyMatching(query
, NULL
), "find tombstone for item we deleted"));
477 CFDictionaryRemoveValue(query
, kSecAttrTombstone
);
479 test_find_managedconfiguration_item(); // <- 2 tests here
481 // TODO: add a different new item - delete what's not in the syncableBackup?
483 // Do another backup after some changes
484 skip("skipping syncable backup tests", 1,
485 ok_status(_SecKeychainBackupSyncable(keybag
, password
, syncableBackup
, &scratch
), "export items after changes"));
487 skip("skipping syncable backup tests", 0,
488 ok_status(_SecKeychainRestoreSyncable(keybag
, password
, syncableBackup
), "import items"));
490 CFReleaseNull(scratch
);
491 CFReleaseNull(error
);
493 // non-syncable item should (still) be gone -> add should work
494 test_add_managedconfiguration_item();
495 test_find_managedconfiguration_item();
497 // syncable item should have not been restored, because the tombstone was newer than the item in the backup -> copy matching should fail
498 is_status(errSecItemNotFound
, SecItemCopyMatching(query
, NULL
),
499 "find restored item");
500 is_status(errSecItemNotFound
, SecItemDelete(query
), "delete restored item");
502 CFReleaseSafe(syncableBackup
);
503 CFReleaseSafe(keybag
);
504 CFReleaseSafe(eighty
);
505 CFReleaseSafe(pwdata
);
506 CFReleaseSafe(query
);
509 int si_33_keychain_backup(int argc
, char *const *argv
)