]> git.saurik.com Git - apple/security.git/blob - sec/Security/Regressions/secitem/si-33-keychain-backup.c
Security-55471.14.tar.gz
[apple/security.git] / sec / Security / Regressions / secitem / si-33-keychain-backup.c
1 /*
2 * si-33-keychain-backup.c
3 * Security
4 *
5 * Created by Michael Brouwer on 1/30/10.
6 * Copyright 2010 Apple Inc. All rights reserved.
7 *
8 */
9
10 #include <TargetConditionals.h>
11
12 #if TARGET_OS_EMBEDDED && !TARGET_IPHONE_SIMULATOR
13 #define USE_KEYSTORE 1
14 #else /* No AppleKeyStore.kext on this OS. */
15 #define USE_KEYSTORE 0
16 #endif
17
18
19 #include <CoreFoundation/CoreFoundation.h>
20 #include <Security/SecBase.h>
21 #include <Security/SecItem.h>
22 #include <Security/SecInternal.h>
23 #include <Security/SecItemPriv.h>
24 #include <utilities/array_size.h>
25
26 #if USE_KEYSTORE
27 #include <IOKit/IOKitLib.h>
28 #include <Kernel/IOKit/crypto/AppleKeyStoreDefs.h>
29 #endif
30
31 #include <stdlib.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <sys/stat.h>
35 #include <sqlite3.h>
36
37 #include "Security_regressions.h"
38
39 struct test_persistent_s {
40 CFTypeRef persist[2];
41 CFDictionaryRef query;
42 CFDictionaryRef query1;
43 CFDictionaryRef query2;
44 CFMutableDictionaryRef query3;
45 CFMutableDictionaryRef query4;
46 };
47
48 static void test_persistent(struct test_persistent_s *p)
49 {
50 int v_eighty = 80;
51 CFNumberRef eighty = CFNumberCreate(NULL, kCFNumberSInt32Type, &v_eighty);
52 const char *v_data = "test";
53 CFDataRef pwdata = CFDataCreate(NULL, (UInt8 *)v_data, strlen(v_data));
54 const void *keys[] = {
55 kSecClass,
56 kSecAttrServer,
57 kSecAttrAccount,
58 kSecAttrPort,
59 kSecAttrProtocol,
60 kSecAttrAuthenticationType,
61 kSecReturnPersistentRef,
62 kSecValueData
63 };
64 const void *values[] = {
65 kSecClassInternetPassword,
66 CFSTR("zuigt.nl"),
67 CFSTR("frtnbf"),
68 eighty,
69 CFSTR("http"),
70 CFSTR("dflt"),
71 kCFBooleanTrue,
72 pwdata
73 };
74 CFDictionaryRef item = CFDictionaryCreate(NULL, keys, values,
75 array_size(keys), &kCFTypeDictionaryKeyCallBacks,
76 &kCFTypeDictionaryValueCallBacks);
77
78 p->persist[0] = NULL;
79 ok_status(SecItemAdd(item, &p->persist[0]), "add internet password");
80 CFTypeRef results = NULL;
81 CFTypeRef results2 = NULL;
82 SKIP: {
83 skip("no persistent ref", 3, ok(p->persist[0], "got back persistent ref"));
84
85 /* Create a dict with all attrs except the data. */
86 keys[(array_size(keys)) - 2] = kSecReturnAttributes;
87 p->query = CFDictionaryCreate(NULL, keys, values,
88 (array_size(keys)) - 1, &kCFTypeDictionaryKeyCallBacks,
89 &kCFTypeDictionaryValueCallBacks);
90 ok_status(SecItemCopyMatching(p->query, &results), "find internet password by attr");
91
92 const void *keys_persist[] = {
93 kSecReturnAttributes,
94 kSecValuePersistentRef
95 };
96 const void *values_persist[] = {
97 kCFBooleanTrue,
98 p->persist[0]
99 };
100 p->query2 = CFDictionaryCreate(NULL, keys_persist, values_persist,
101 (array_size(keys_persist)), &kCFTypeDictionaryKeyCallBacks,
102 &kCFTypeDictionaryValueCallBacks);
103 ok_status(SecItemCopyMatching(p->query2, &results2), "find internet password by persistent ref");
104 ok(CFEqual(results, results2 ? results2 : CFSTR("")), "same item (attributes)");
105
106 CFReleaseNull(results);
107 CFReleaseNull(results2);
108 }
109 ok_status(SecItemDelete(p->query), "delete internet password");
110
111 ok_status(!SecItemCopyMatching(p->query, &results),
112 "don't find internet password by attributes");
113 ok(!results, "no results");
114
115 /* clean up left over from aborted run */
116 if (results) {
117 CFDictionaryRef cleanup = CFDictionaryCreate(NULL, &kSecValuePersistentRef,
118 &results, 1, &kCFTypeDictionaryKeyCallBacks,
119 &kCFTypeDictionaryValueCallBacks);
120 SecItemDelete(cleanup);
121 CFRelease(results);
122 CFRelease(cleanup);
123 }
124
125 ok_status(!SecItemCopyMatching(p->query2, &results2),
126 "don't find internet password by persistent ref anymore");
127 ok(!results2, "no results");
128
129 CFReleaseNull(p->persist[0]);
130
131 /* Add a new item and get it's persitant ref. */
132 ok_status(SecItemAdd(item, &p->persist[0]), "add internet password");
133 p->persist[1] = NULL;
134 CFMutableDictionaryRef item2 = CFDictionaryCreateMutableCopy(NULL, 0, item);
135 CFDictionarySetValue(item2, kSecAttrAccount, CFSTR("johndoe-bu"));
136 ok_status(SecItemAdd(item2, &p->persist[1]), "add second internet password");
137 is(CFGetTypeID(p->persist[0]), CFDataGetTypeID(), "result is a CFData");
138 p->query3 = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
139 &kCFTypeDictionaryValueCallBacks);
140 CFDictionaryAddValue(p->query3, kSecValuePersistentRef, p->persist[0]);
141 CFMutableDictionaryRef update = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
142 CFDictionaryAddValue(update, kSecAttrServer, CFSTR("zuigt.com"));
143 ok_status(SecItemUpdate(p->query3, update), "update via persitant ref");
144
145 /* Verify that the update really worked. */
146 CFDictionaryAddValue(p->query3, kSecReturnAttributes, kCFBooleanTrue);
147 ok_status(SecItemCopyMatching(p->query3, &results2), "find updated internet password by persistent ref");
148 CFStringRef server = CFDictionaryGetValue(results2, kSecAttrServer);
149 ok(CFEqual(server, CFSTR("zuigt.com")), "verify attribute was modified by update");
150 CFReleaseNull(results2);
151 CFDictionaryRemoveValue(p->query3, kSecReturnAttributes);
152
153 /* Verify that item2 wasn't affected by the update. */
154 p->query4 = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
155 &kCFTypeDictionaryValueCallBacks);
156 CFDictionaryAddValue(p->query4, kSecValuePersistentRef, p->persist[1]);
157 CFDictionaryAddValue(p->query4, kSecReturnAttributes, kCFBooleanTrue);
158 ok_status(SecItemCopyMatching(p->query4, &results2), "find non updated internet password by persistent ref");
159 server = CFDictionaryGetValue(results2, kSecAttrServer);
160 ok(CFEqual(server, CFSTR("zuigt.nl")), "verify second items attribute was not modified by update");
161 CFReleaseNull(results2);
162
163 /* Delete the item via persitant ref. */
164 ok_status(SecItemDelete(p->query3), "delete via persitant ref");
165 is_status(SecItemCopyMatching(p->query3, &results2), errSecItemNotFound,
166 "don't find deleted internet password by persistent ref");
167 CFReleaseNull(results2);
168 ok_status(SecItemCopyMatching(p->query4, &results2),
169 "find non deleted internet password by persistent ref");
170 CFReleaseNull(results2);
171
172 CFRelease(update);
173 CFReleaseNull(item);
174 CFReleaseNull(item2);
175 CFReleaseNull(eighty);
176 CFReleaseNull(pwdata);
177 }
178
179 static void test_persistent2(struct test_persistent_s *p)
180 {
181 CFTypeRef results = NULL;
182 CFTypeRef results2 = NULL;
183
184 ok_status(!SecItemCopyMatching(p->query, &results),
185 "don't find internet password by attributes");
186 ok(!results, "no results");
187
188 ok_status(!SecItemCopyMatching(p->query2, &results2),
189 "don't find internet password by persistent ref anymore");
190 ok(!results2, "no results");
191
192 SKIP:{
193 ok_status(SecItemCopyMatching(p->query4, &results2), "find non updated internet password by persistent ref");
194 skip("non updated internet password by persistent ref NOT FOUND!", 2, results2);
195 ok(results2, "non updated internet password not found");
196 CFStringRef server = CFDictionaryGetValue(results2, kSecAttrServer);
197 ok(CFEqual(server, CFSTR("zuigt.nl")), "verify second items attribute was not modified by update");
198 CFReleaseNull(results2);
199 }
200
201 is_status(SecItemCopyMatching(p->query3, &results2), errSecItemNotFound,
202 "don't find deleted internet password by persistent ref");
203 CFReleaseNull(results2);
204 ok_status(SecItemCopyMatching(p->query4, &results2),
205 "find non deleted internet password by persistent ref");
206 CFReleaseNull(results2);
207
208 ok_status(SecItemDelete(p->query4),"Deleted internet password by persistent ref");
209
210 CFRelease(p->query);
211 CFRelease(p->query2);
212 CFRelease(p->query3);
213 CFRelease(p->query4);
214 CFReleaseNull(p->persist[0]);
215 CFReleaseNull(p->persist[1]);
216 }
217
218 static CFMutableDictionaryRef test_create_lockdown_identity_query(void) {
219 CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
220 CFDictionaryAddValue(query, kSecClass, kSecClassGenericPassword);
221 CFDictionaryAddValue(query, kSecAttrAccessGroup, CFSTR("lockdown-identities"));
222 return query;
223 }
224
225 static CFMutableDictionaryRef test_create_managedconfiguration_query(void) {
226 CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
227 CFDictionaryAddValue(query, kSecClass, kSecClassGenericPassword);
228 CFDictionaryAddValue(query, kSecAttrService, CFSTR("com.apple.managedconfiguration"));
229 CFDictionaryAddValue(query, kSecAttrAccount, CFSTR("Public"));
230 CFDictionaryAddValue(query, kSecAttrAccessGroup, CFSTR("apple"));
231 return query;
232 }
233
234 static void test_add_lockdown_identity_items(void) {
235 CFMutableDictionaryRef query = test_create_lockdown_identity_query();
236 const char *v_data = "lockdown identity data (which should be a cert + key)";
237 CFDataRef pwdata = CFDataCreate(NULL, (UInt8 *)v_data, strlen(v_data));
238 CFDictionaryAddValue(query, kSecValueData, pwdata);
239 ok_status(SecItemAdd(query, NULL), "test_add_lockdown_identity_items");
240 CFReleaseSafe(pwdata);
241 CFReleaseSafe(query);
242 }
243
244 static void test_remove_lockdown_identity_items(void) {
245 CFMutableDictionaryRef query = test_create_lockdown_identity_query();
246 ok_status(SecItemDelete(query), "test_remove_lockdown_identity_items");
247 CFReleaseSafe(query);
248 }
249
250 static void test_no_find_lockdown_identity_item(void) {
251 CFMutableDictionaryRef query = test_create_lockdown_identity_query();
252 is_status(SecItemCopyMatching(query, NULL), errSecItemNotFound,
253 "test_no_find_lockdown_identity_item");
254 CFReleaseSafe(query);
255 }
256
257 static void test_add_managedconfiguration_item(void) {
258 CFMutableDictionaryRef query = test_create_managedconfiguration_query();
259 const char *v_data = "public managedconfiguration password history data";
260 CFDataRef pwdata = CFDataCreate(NULL, (UInt8 *)v_data, strlen(v_data));
261 CFDictionaryAddValue(query, kSecValueData, pwdata);
262 ok_status(SecItemAdd(query, NULL), "test_add_managedconfiguration_item");
263 CFReleaseSafe(pwdata);
264 CFReleaseSafe(query);
265 }
266
267 static void test_find_managedconfiguration_item(void) {
268 CFMutableDictionaryRef query = test_create_managedconfiguration_query();
269 ok_status(SecItemCopyMatching(query, NULL), "test_find_managedconfiguration_item");
270 ok_status(SecItemDelete(query), "test_find_managedconfiguration_item (deleted)");
271 CFReleaseSafe(query);
272 }
273
274 #if USE_KEYSTORE
275 static io_connect_t connect_to_keystore(void)
276 {
277 io_registry_entry_t apple_key_bag_service;
278 kern_return_t result;
279 io_connect_t keystore = MACH_PORT_NULL;
280
281 apple_key_bag_service = IOServiceGetMatchingService(kIOMasterPortDefault,
282 IOServiceMatching(kAppleKeyStoreServiceName));
283
284 if (apple_key_bag_service == IO_OBJECT_NULL) {
285 fprintf(stderr, "Failed to get service.\n");
286 return keystore;
287 }
288
289 result = IOServiceOpen(apple_key_bag_service, mach_task_self(), 0, &keystore);
290 if (KERN_SUCCESS != result)
291 fprintf(stderr, "Failed to open keystore\n");
292
293 if (keystore != MACH_PORT_NULL) {
294 IOReturn kernResult = IOConnectCallMethod(keystore,
295 kAppleKeyStoreUserClientOpen, NULL, 0, NULL, 0, NULL, NULL,
296 NULL, NULL);
297 if (kernResult) {
298 fprintf(stderr, "Failed to open AppleKeyStore: %x\n", kernResult);
299 }
300 }
301 return keystore;
302 }
303
304 static CFDataRef create_keybag(keybag_handle_t bag_type, CFDataRef password)
305 {
306 uint64_t inputs[] = { bag_type };
307 uint64_t outputs[] = {0};
308 uint32_t num_inputs = array_size(inputs);
309 uint32_t num_outputs = array_size(outputs);
310 IOReturn kernResult;
311
312 io_connect_t keystore;
313
314 unsigned char keybagdata[4096]; //Is that big enough?
315 size_t keybagsize=sizeof(keybagdata);
316
317 keystore=connect_to_keystore();
318
319 kernResult = IOConnectCallMethod(keystore,
320 kAppleKeyStoreKeyBagCreate,
321 inputs, num_inputs, NULL, 0,
322 outputs, &num_outputs, NULL, 0);
323
324 if (kernResult) {
325 fprintf(stderr, "kAppleKeyStoreKeyBagCreate: 0x%x\n", kernResult);
326 return NULL;
327 }
328
329 /* Copy out keybag */
330 inputs[0]=outputs[0];
331 num_inputs=1;
332
333 kernResult = IOConnectCallMethod(keystore,
334 kAppleKeyStoreKeyBagCopy,
335 inputs, num_inputs, NULL, 0,
336 NULL, 0, keybagdata, &keybagsize);
337
338 if (kernResult) {
339 fprintf(stderr, "kAppleKeyStoreKeyBagCopy: 0x%x\n", kernResult);
340 return NULL;
341 }
342
343 return CFDataCreate(kCFAllocatorDefault, keybagdata, keybagsize);
344 }
345 #endif
346
347 /* Test low level keychain migration from device to device interface. */
348 static void tests(void)
349 {
350 int v_eighty = 80;
351 CFNumberRef eighty = CFNumberCreate(NULL, kCFNumberSInt32Type, &v_eighty);
352 const char *v_data = "test";
353 CFDataRef pwdata = CFDataCreate(NULL, (UInt8 *)v_data, strlen(v_data));
354 CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
355 CFDictionaryAddValue(query, kSecClass, kSecClassInternetPassword);
356 CFDictionaryAddValue(query, kSecAttrServer, CFSTR("members.spamcop.net"));
357 CFDictionaryAddValue(query, kSecAttrAccount, CFSTR("smith"));
358 CFDictionaryAddValue(query, kSecAttrPort, eighty);
359 CFDictionaryAddValue(query, kSecAttrProtocol, kSecAttrProtocolHTTP);
360 CFDictionaryAddValue(query, kSecAttrAuthenticationType, kSecAttrAuthenticationTypeDefault);
361 CFDictionaryAddValue(query, kSecValueData, pwdata);
362 ok_status(SecItemAdd(query, NULL), "add internet password");
363 is_status(SecItemAdd(query, NULL), errSecDuplicateItem,
364 "add internet password again");
365
366 ok_status(SecItemCopyMatching(query, NULL), "Found the item we added");
367
368 struct test_persistent_s p = {};
369 test_persistent(&p);
370
371 CFDataRef backup = NULL, keybag = NULL, password = NULL;
372
373 test_add_lockdown_identity_items();
374
375 #if USE_KEYSTORE
376 keybag = create_keybag(kAppleKeyStoreBackupBag, password);
377 #else
378 keybag = CFDataCreate(kCFAllocatorDefault, NULL, 0);
379 #endif
380
381 ok(backup = _SecKeychainCopyBackup(keybag, password),
382 "_SecKeychainCopyBackup");
383
384 test_add_managedconfiguration_item();
385 test_remove_lockdown_identity_items();
386
387 ok_status(_SecKeychainRestoreBackup(backup, keybag, password),
388 "_SecKeychainRestoreBackup");
389 CFReleaseSafe(backup);
390
391 test_no_find_lockdown_identity_item();
392 test_find_managedconfiguration_item();
393
394 ok_status(SecItemCopyMatching(query, NULL),
395 "Found the item we added after restore");
396
397 test_persistent2(&p);
398
399 #if USE_KEYSTORE
400 CFReleaseNull(keybag);
401 keybag = create_keybag(kAppleKeyStoreOTABackupBag, password);
402 #endif
403
404 ok(backup = _SecKeychainCopyBackup(keybag, password),
405 "_SecKeychainCopyBackup");
406 ok_status(_SecKeychainRestoreBackup(backup, keybag, password),
407 "_SecKeychainRestoreBackup");
408 ok_status(SecItemCopyMatching(query, NULL),
409 "Found the item we added after restore");
410 CFReleaseNull(backup);
411
412 CFDictionaryAddValue(query, kSecUseTombstones, kCFBooleanTrue);
413
414 ok_status(SecItemDelete(query), "Deleted item we added");
415
416 #if USE_KEYSTORE
417 CFReleaseNull(keybag);
418 keybag = create_keybag(kAppleKeyStoreOTABackupBag /* use truthiness bag once it's there */, password);
419 #endif
420
421 // add syncable item
422 CFDictionaryAddValue(query, kSecAttrSynchronizable, kCFBooleanTrue);
423 ok_status(SecItemAdd(query, NULL), "add internet password");
424
425 // and non-syncable item
426 test_add_managedconfiguration_item();
427
428 CFDictionaryRef syncableBackup = NULL;
429
430 ok_status(_SecKeychainBackupSyncable(keybag, password, NULL, &syncableBackup), "export items");
431
432 // TODO: add item, call SecServerCopyTruthInTheCloud again
433
434 // CFShow(syncableBackup);
435
436 // find and delete
437 ok_status(SecItemCopyMatching(query, NULL), "find item we are about to destroy");
438
439 ok_status(SecItemDelete(query), "delete item we backed up");
440
441 // ensure we added a tombstone
442 CFDictionaryAddValue(query, kSecAttrTombstone, kCFBooleanTrue);
443 ok_status(SecItemCopyMatching(query, NULL), "find tombstone for item we deleted");
444 CFDictionaryRemoveValue(query, kSecAttrTombstone);
445
446 test_find_managedconfiguration_item();
447
448 // TODO: add a different new item - delete what's not in the syncableBackup?
449
450 // Do another backup after some changes
451 CFDictionaryRef scratch = NULL;
452 ok_status(_SecKeychainBackupSyncable(keybag, password, syncableBackup, &scratch), "export items after changes");
453 CFReleaseNull(scratch);
454
455 ok_status(_SecKeychainRestoreSyncable(keybag, password, syncableBackup), "import items");
456
457 // non-syncable item should (still) be gone -> add should work
458 test_add_managedconfiguration_item();
459 test_find_managedconfiguration_item();
460
461 // syncable item should have not been restored, because the tombstone was newer than the item in the backup -> copy matching should fail
462 is_status(errSecItemNotFound, SecItemCopyMatching(query, NULL),
463 "find restored item");
464 is_status(errSecItemNotFound, SecItemDelete(query), "delete restored item");
465
466 CFReleaseSafe(syncableBackup);
467 CFReleaseSafe(keybag);
468 CFReleaseSafe(eighty);
469 CFReleaseSafe(pwdata);
470 CFReleaseSafe(query);
471 }
472
473 int si_33_keychain_backup(int argc, char *const *argv)
474 {
475 plan_tests(62);
476
477
478 tests();
479
480 return 0;
481 }