]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/Regressions/secitem/si-33-keychain-backup.c
Security-57740.60.18.tar.gz
[apple/security.git] / OSX / sec / Security / Regressions / secitem / si-33-keychain-backup.c
1 /*
2 * Copyright (c) 2010,2012-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 #include <TargetConditionals.h>
26
27 #if TARGET_OS_EMBEDDED && !TARGET_IPHONE_SIMULATOR
28 #define USE_KEYSTORE 1
29 #else /* No AppleKeyStore.kext on this OS. */
30 #define USE_KEYSTORE 0
31 #endif
32
33
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>
40
41 #if USE_KEYSTORE
42 #include <IOKit/IOKitLib.h>
43 #include <Kernel/IOKit/crypto/AppleKeyStoreDefs.h>
44 #endif
45
46 #include <stdlib.h>
47 #include <fcntl.h>
48 #include <unistd.h>
49 #include <sys/stat.h>
50 #include <sqlite3.h>
51
52 #include "../Security_regressions.h"
53
54 struct test_persistent_s {
55 CFTypeRef persist[2];
56 CFDictionaryRef query;
57 CFDictionaryRef query1;
58 CFDictionaryRef query2;
59 CFMutableDictionaryRef query3;
60 CFMutableDictionaryRef query4;
61 };
62
63 static void test_persistent(struct test_persistent_s *p)
64 {
65 int v_eighty = 80;
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[] = {
70 kSecClass,
71 kSecAttrServer,
72 kSecAttrAccount,
73 kSecAttrPort,
74 kSecAttrProtocol,
75 kSecAttrAuthenticationType,
76 kSecReturnPersistentRef,
77 kSecValueData
78 };
79 const void *values[] = {
80 kSecClassInternetPassword,
81 CFSTR("zuigt.nl"),
82 CFSTR("frtnbf"),
83 eighty,
84 CFSTR("http"),
85 CFSTR("dflt"),
86 kCFBooleanTrue,
87 pwdata
88 };
89 CFDictionaryRef item = CFDictionaryCreate(NULL, keys, values,
90 array_size(keys), &kCFTypeDictionaryKeyCallBacks,
91 &kCFTypeDictionaryValueCallBacks);
92
93 p->persist[0] = NULL;
94 // NUKE anything we might have left around from a previous test run so we don't crash.
95 SecItemDelete(item);
96 ok_status(SecItemAdd(item, &p->persist[0]), "add internet password");
97 CFTypeRef results = NULL;
98 CFTypeRef results2 = NULL;
99 SKIP: {
100 skip("no persistent ref", 6, ok(p->persist[0], "got back persistent ref"));
101
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");
108
109 const void *keys_persist[] = {
110 kSecReturnAttributes,
111 kSecValuePersistentRef
112 };
113 const void *values_persist[] = {
114 kCFBooleanTrue,
115 p->persist[0]
116 };
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)");
122
123 CFReleaseNull(results);
124 CFReleaseNull(results2);
125
126 ok_status(SecItemDelete(p->query), "delete internet password");
127
128 ok_status(!SecItemCopyMatching(p->query, &results),
129 "don't find internet password by attributes");
130 ok(!results, "no results");
131 }
132
133 /* clean up left over from aborted run */
134 if (results) {
135 CFDictionaryRef cleanup = CFDictionaryCreate(NULL, (const void **)&kSecValuePersistentRef,
136 &results, 1, &kCFTypeDictionaryKeyCallBacks,
137 &kCFTypeDictionaryValueCallBacks);
138 SecItemDelete(cleanup);
139 CFRelease(results);
140 CFRelease(cleanup);
141 }
142
143 ok_status(!SecItemCopyMatching(p->query2, &results2),
144 "don't find internet password by persistent ref anymore");
145 ok(!results2, "no results");
146
147 CFReleaseNull(p->persist[0]);
148
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;
159 SKIP: {
160 skip("no persistent ref", 3, ok(p->persist[0], "got back persistent ref from first internet password"));
161
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");
169
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);
177 }
178
179 SKIP: {
180 skip("no persistent ref", 2, ok(p->persist[1], "got back persistent ref"));
181
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);
191 }
192
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);
201
202 CFReleaseNull(update);
203 CFReleaseNull(item);
204 CFReleaseNull(item2);
205 CFReleaseNull(eighty);
206 CFReleaseNull(pwdata);
207 }
208
209 static void test_persistent2(struct test_persistent_s *p)
210 {
211 CFTypeRef results = NULL;
212 CFTypeRef results2 = NULL;
213
214 ok_status(!SecItemCopyMatching(p->query, &results),
215 "don't find internet password by attributes");
216 ok(!results, "no results");
217
218 ok_status(!SecItemCopyMatching(p->query2, &results2),
219 "don't find internet password by persistent ref anymore");
220 ok(!results2, "no results");
221
222 SKIP:{
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);
229 }
230
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);
237
238 ok_status(SecItemDelete(p->query4),"Deleted internet password by persistent ref");
239
240 CFRelease(p->query);
241 CFRelease(p->query2);
242 CFRelease(p->query3);
243 CFRelease(p->query4);
244 CFReleaseNull(p->persist[0]);
245 CFReleaseNull(p->persist[1]);
246 }
247
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"));
253 return query;
254 }
255
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"));
262 return query;
263 }
264
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);
273 }
274
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);
279 }
280
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);
286 }
287
288 static void test_add_managedconfiguration_item(void) {
289 CFMutableDictionaryRef query = test_create_managedconfiguration_query();
290 const char *v_data = "public managedconfiguration password history data";
291 CFDataRef pwdata = CFDataCreate(NULL, (UInt8 *)v_data, strlen(v_data));
292 CFDictionaryAddValue(query, kSecValueData, pwdata);
293 ok_status(SecItemAdd(query, NULL), "test_add_managedconfiguration_item");
294 CFReleaseSafe(pwdata);
295 CFReleaseSafe(query);
296 }
297
298 static void test_find_managedconfiguration_item(void) {
299 CFMutableDictionaryRef query = test_create_managedconfiguration_query();
300 ok_status(SecItemCopyMatching(query, NULL), "test_find_managedconfiguration_item");
301 ok_status(SecItemDelete(query), "test_find_managedconfiguration_item (deleted)");
302 CFReleaseSafe(query);
303 }
304
305 #if USE_KEYSTORE
306 static io_connect_t connect_to_keystore(void)
307 {
308 io_registry_entry_t apple_key_bag_service;
309 kern_return_t result;
310 io_connect_t keystore = MACH_PORT_NULL;
311
312 apple_key_bag_service = IOServiceGetMatchingService(kIOMasterPortDefault,
313 IOServiceMatching(kAppleKeyStoreServiceName));
314
315 if (apple_key_bag_service == IO_OBJECT_NULL) {
316 fprintf(stderr, "Failed to get service.\n");
317 return keystore;
318 }
319
320 result = IOServiceOpen(apple_key_bag_service, mach_task_self(), 0, &keystore);
321 if (KERN_SUCCESS != result)
322 fprintf(stderr, "Failed to open keystore\n");
323
324 if (keystore != MACH_PORT_NULL) {
325 IOReturn kernResult = IOConnectCallMethod(keystore,
326 kAppleKeyStoreUserClientOpen, NULL, 0, NULL, 0, NULL, NULL,
327 NULL, NULL);
328 if (kernResult) {
329 fprintf(stderr, "Failed to open AppleKeyStore: %x\n", kernResult);
330 }
331 }
332 return keystore;
333 }
334 #define DATA_ARG(x) (x) ? CFDataGetBytePtr((x)) : NULL, (x) ? (int)CFDataGetLength((x)) : 0
335
336 static CFDataRef create_keybag(keybag_handle_t bag_type, CFDataRef password)
337 {
338 uint64_t inputs[] = { bag_type };
339 uint64_t outputs[] = {0};
340 uint32_t num_inputs = array_size(inputs);
341 uint32_t num_outputs = array_size(outputs);
342 IOReturn kernResult;
343
344 io_connect_t keystore;
345
346 unsigned char keybagdata[4096]; //Is that big enough?
347 size_t keybagsize=sizeof(keybagdata);
348
349 keystore=connect_to_keystore();
350
351 kernResult = IOConnectCallMethod(keystore,
352 kAppleKeyStoreKeyBagCreate,
353 inputs, num_inputs, DATA_ARG(password),
354 outputs, &num_outputs, NULL, 0);
355
356 if (kernResult) {
357 fprintf(stderr, "kAppleKeyStoreKeyBagCreate: 0x%x\n", kernResult);
358 return NULL;
359 }
360
361 /* Copy out keybag */
362 inputs[0]=outputs[0];
363 num_inputs=1;
364
365 kernResult = IOConnectCallMethod(keystore,
366 kAppleKeyStoreKeyBagCopy,
367 inputs, num_inputs, NULL, 0,
368 NULL, 0, keybagdata, &keybagsize);
369
370 if (kernResult) {
371 fprintf(stderr, "kAppleKeyStoreKeyBagCopy: 0x%x\n", kernResult);
372 return NULL;
373 }
374
375 return CFDataCreate(kCFAllocatorDefault, keybagdata, keybagsize);
376 }
377 #endif
378
379 /* Test low level keychain migration from device to device interface. */
380 static void tests(void)
381 {
382 {
383 CFMutableDictionaryRef lock_down_query = test_create_lockdown_identity_query();
384 (void)SecItemDelete(lock_down_query);
385 CFReleaseNull(lock_down_query);
386 }
387
388 int v_eighty = 80;
389 CFNumberRef eighty = CFNumberCreate(NULL, kCFNumberSInt32Type, &v_eighty);
390 const char *v_data = "test";
391 CFDataRef pwdata = CFDataCreate(NULL, (UInt8 *)v_data, strlen(v_data));
392 CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
393 CFDictionaryAddValue(query, kSecClass, kSecClassInternetPassword);
394 CFDictionaryAddValue(query, kSecAttrServer, CFSTR("members.spamcop.net"));
395 CFDictionaryAddValue(query, kSecAttrAccount, CFSTR("smith"));
396 CFDictionaryAddValue(query, kSecAttrPort, eighty);
397 CFDictionaryAddValue(query, kSecAttrProtocol, kSecAttrProtocolHTTP);
398 CFDictionaryAddValue(query, kSecAttrAuthenticationType, kSecAttrAuthenticationTypeDefault);
399 CFDictionaryAddValue(query, kSecValueData, pwdata);
400 // NUKE anything we might have left around from a previous test run so we don't crash.
401 (void)SecItemDelete(query);
402
403 ok_status(SecItemAdd(query, NULL), "add internet password");
404 is_status(SecItemAdd(query, NULL), errSecDuplicateItem,
405 "add internet password again");
406
407 ok_status(SecItemCopyMatching(query, NULL), "Found the item we added");
408
409 struct test_persistent_s p = {};
410 test_persistent(&p);
411
412 CFDataRef backup = NULL, keybag = NULL, password = NULL;
413
414 test_add_lockdown_identity_items();
415
416 #if USE_KEYSTORE
417 keybag = create_keybag(kAppleKeyStoreBackupBag, password);
418 #else
419 keybag = CFDataCreate(kCFAllocatorDefault, NULL, 0);
420 #endif
421
422 ok(backup = _SecKeychainCopyBackup(keybag, password),
423 "_SecKeychainCopyBackup");
424
425 test_add_managedconfiguration_item();
426 test_remove_lockdown_identity_items();
427
428 ok_status(_SecKeychainRestoreBackup(backup, keybag, password),
429 "_SecKeychainRestoreBackup");
430 CFReleaseSafe(backup);
431
432 test_no_find_lockdown_identity_item();
433 test_find_managedconfiguration_item();
434
435 ok_status(SecItemCopyMatching(query, NULL),
436 "Found the item we added after restore");
437
438 test_persistent2(&p);
439
440 #if USE_KEYSTORE
441 CFReleaseNull(keybag);
442 keybag = create_keybag(kAppleKeyStoreOTABackupBag, password);
443 #endif
444
445 ok(backup = _SecKeychainCopyBackup(keybag, password),
446 "_SecKeychainCopyBackup");
447 ok_status(_SecKeychainRestoreBackup(backup, keybag, password),
448 "_SecKeychainRestoreBackup");
449 ok_status(SecItemCopyMatching(query, NULL),
450 "Found the item we added after restore");
451 CFReleaseNull(backup);
452
453 // force tombstone to be added, since it's not the default behavior per rdar://14680869
454 CFDictionaryAddValue(query, kSecUseTombstones, kCFBooleanTrue);
455
456 ok_status(SecItemDelete(query), "Deleted item we added");
457
458 #if USE_KEYSTORE
459 CFReleaseNull(keybag);
460 keybag = create_keybag(kAppleKeyStoreOTABackupBag /* use truthiness bag once it's there */, password);
461 #endif
462
463 // add syncable item
464 CFDictionaryAddValue(query, kSecAttrSynchronizable, kCFBooleanTrue);
465 ok_status(SecItemAdd(query, NULL), "add internet password");
466
467 // and non-syncable item
468 test_add_managedconfiguration_item();
469
470 CFDictionaryRef syncableBackup = NULL;
471
472 CFErrorRef error = NULL;
473 CFDictionaryRef scratch = NULL;
474 SKIP: {
475 skip("skipping syncable backup tests", 7,
476 ok_status(_SecKeychainBackupSyncable(keybag, password, NULL, &syncableBackup), "export items"));
477
478 // TODO: add item, call SecServerCopyTruthInTheCloud again
479
480 // CFShow(syncableBackup);
481
482 // find and delete
483 skip("skipping syncable backup tests", 6,
484 ok_status(SecItemCopyMatching(query, NULL), "find item we are about to destroy"));
485
486 skip("skipping syncable backup tests", 5,
487 ok_status(SecItemDelete(query), "delete item we backed up"));
488
489 // ensure we added a tombstone
490 CFDictionaryAddValue(query, kSecAttrTombstone, kCFBooleanTrue);
491 skip("skipping syncable backup tests", 4,
492 ok_status(SecItemCopyMatching(query, NULL), "find tombstone for item we deleted"));
493 CFDictionaryRemoveValue(query, kSecAttrTombstone);
494
495 test_find_managedconfiguration_item(); // <- 2 tests here
496
497 // TODO: add a different new item - delete what's not in the syncableBackup?
498
499 // Do another backup after some changes
500 skip("skipping syncable backup tests", 1,
501 ok_status(_SecKeychainBackupSyncable(keybag, password, syncableBackup, &scratch), "export items after changes"));
502
503 skip("skipping syncable backup tests", 0,
504 ok_status(_SecKeychainRestoreSyncable(keybag, password, syncableBackup), "import items"));
505 }
506 CFReleaseNull(scratch);
507 CFReleaseNull(error);
508
509 // non-syncable item should (still) be gone -> add should work
510 test_add_managedconfiguration_item();
511 test_find_managedconfiguration_item();
512
513 // syncable item should have not been restored, because the tombstone was newer than the item in the backup -> copy matching should fail
514 is_status(errSecItemNotFound, SecItemCopyMatching(query, NULL),
515 "find restored item");
516 is_status(errSecItemNotFound, SecItemDelete(query), "delete restored item");
517
518 CFReleaseSafe(syncableBackup);
519 CFReleaseSafe(keybag);
520 CFReleaseSafe(eighty);
521 CFReleaseSafe(pwdata);
522 CFReleaseSafe(query);
523 }
524
525 int si_33_keychain_backup(int argc, char *const *argv)
526 {
527 plan_tests(64);
528
529 tests();
530
531 return 0;
532 }