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 #include "securityd/SecKeybagSupport.h"
29 #include <CoreFoundation/CoreFoundation.h>
30 #include <Security/SecBase.h>
31 #include <Security/SecItem.h>
32 #include <Security/SecInternal.h>
33 #include <Security/SecItemPriv.h>
34 #include <utilities/array_size.h>
35 #include <utilities/SecCFWrappers.h>
38 #include <AssertMacros.h>
39 #include "OSX/utilities/SecAKSWrappers.h"
48 #include "../Security_regressions.h"
50 struct test_persistent_s
{
52 CFDictionaryRef query
;
53 CFDictionaryRef query1
;
54 CFDictionaryRef query2
;
55 CFMutableDictionaryRef query3
;
56 CFMutableDictionaryRef query4
;
59 static void test_persistent(struct test_persistent_s
*p
)
62 CFNumberRef eighty
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &v_eighty
);
63 const char *v_data
= "test";
64 CFDataRef pwdata
= CFDataCreate(NULL
, (UInt8
*)v_data
, strlen(v_data
));
65 const void *keys
[] = {
71 kSecAttrAuthenticationType
,
72 kSecReturnPersistentRef
,
75 const void *values
[] = {
76 kSecClassInternetPassword
,
85 CFDictionaryRef item
= CFDictionaryCreate(NULL
, keys
, values
,
86 array_size(keys
), &kCFTypeDictionaryKeyCallBacks
,
87 &kCFTypeDictionaryValueCallBacks
);
90 // NUKE anything we might have left around from a previous test run so we don't crash.
92 ok_status(SecItemAdd(item
, &p
->persist
[0]), "add internet password");
93 CFTypeRef results
= NULL
;
94 CFTypeRef results2
= NULL
;
96 skip("no persistent ref", 6, ok(p
->persist
[0], "got back persistent ref"));
98 /* Create a dict with all attrs except the data. */
99 keys
[(array_size(keys
)) - 2] = kSecReturnAttributes
;
100 p
->query
= CFDictionaryCreate(NULL
, keys
, values
,
101 (array_size(keys
)) - 1, &kCFTypeDictionaryKeyCallBacks
,
102 &kCFTypeDictionaryValueCallBacks
);
103 ok_status(SecItemCopyMatching(p
->query
, &results
), "find internet password by attr");
105 const void *keys_persist
[] = {
106 kSecReturnAttributes
,
107 kSecValuePersistentRef
109 const void *values_persist
[] = {
113 p
->query2
= CFDictionaryCreate(NULL
, keys_persist
, values_persist
,
114 (array_size(keys_persist
)), &kCFTypeDictionaryKeyCallBacks
,
115 &kCFTypeDictionaryValueCallBacks
);
116 ok_status(SecItemCopyMatching(p
->query2
, &results2
), "find internet password by persistent ref");
117 ok(CFEqual(results
, results2
? results2
: CFSTR("")), "same item (attributes)");
119 CFReleaseNull(results
);
120 CFReleaseNull(results2
);
122 ok_status(SecItemDelete(p
->query
), "delete internet password");
124 ok_status(!SecItemCopyMatching(p
->query
, &results
),
125 "don't find internet password by attributes");
126 ok(!results
, "no results");
129 /* clean up left over from aborted run */
131 CFDictionaryRef cleanup
= CFDictionaryCreate(NULL
, (const void **)&kSecValuePersistentRef
,
132 &results
, 1, &kCFTypeDictionaryKeyCallBacks
,
133 &kCFTypeDictionaryValueCallBacks
);
134 SecItemDelete(cleanup
);
139 ok_status(!SecItemCopyMatching(p
->query2
, &results2
),
140 "don't find internet password by persistent ref anymore");
141 ok(!results2
, "no results");
143 CFReleaseNull(p
->persist
[0]);
145 /* Add a new item and get it's persitant ref. */
146 ok_status(SecItemAdd(item
, &p
->persist
[0]), "add internet password");
147 p
->persist
[1] = NULL
;
148 CFMutableDictionaryRef item2
= CFDictionaryCreateMutableCopy(NULL
, 0, item
);
149 CFDictionarySetValue(item2
, kSecAttrAccount
, CFSTR("johndoe-bu"));
150 // NUKE anything we might have left around from a previous test run so we don't crash.
151 SecItemDelete(item2
);
152 ok_status(SecItemAdd(item2
, &p
->persist
[1]), "add second internet password");
153 CFMutableDictionaryRef update
= NULL
;
154 CFStringRef server
= NULL
;
156 skip("no persistent ref", 3, ok(p
->persist
[0], "got back persistent ref from first internet password"));
158 is(CFGetTypeID(p
->persist
[0]), CFDataGetTypeID(), "result is a CFData");
159 p
->query3
= CFDictionaryCreateMutableForCFTypes(NULL
);
160 CFDictionaryAddValue(p
->query3
, kSecValuePersistentRef
, p
->persist
[0]);
161 update
= CFDictionaryCreateMutableForCFTypes(NULL
);
162 CFDictionaryAddValue(update
, kSecAttrServer
, CFSTR("zuigt.com"));
163 ok_status(SecItemUpdate(p
->query3
, update
), "update via persitant ref");
165 /* Verify that the update really worked. */
166 CFDictionaryAddValue(p
->query3
, kSecReturnAttributes
, kCFBooleanTrue
);
167 ok_status(SecItemCopyMatching(p
->query3
, &results2
), "find updated internet password by persistent ref");
168 server
= CFDictionaryGetValue(results2
, kSecAttrServer
);
169 ok(CFEqual(server
, CFSTR("zuigt.com")), "verify attribute was modified by update");
170 CFReleaseNull(results2
);
171 CFDictionaryRemoveValue(p
->query3
, kSecReturnAttributes
);
175 skip("no persistent ref", 2, ok(p
->persist
[1], "got back persistent ref"));
177 /* Verify that item2 wasn't affected by the update. */
178 p
->query4
= CFDictionaryCreateMutableForCFTypes(NULL
);
179 CFDictionaryAddValue(p
->query4
, kSecValuePersistentRef
, p
->persist
[1]);
180 CFDictionaryAddValue(p
->query4
, kSecReturnAttributes
, kCFBooleanTrue
);
181 ok_status(SecItemCopyMatching(p
->query4
, &results2
), "find non updated internet password by persistent ref");
182 server
= CFDictionaryGetValue(results2
, kSecAttrServer
);
183 ok(CFEqual(server
, CFSTR("zuigt.nl")), "verify second items attribute was not modified by update");
184 CFReleaseNull(results2
);
187 /* Delete the item via persitant ref. */
188 ok_status(SecItemDelete(p
->query3
), "delete via persitant ref");
189 is_status(SecItemCopyMatching(p
->query3
, &results2
), errSecItemNotFound
,
190 "don't find deleted internet password by persistent ref");
191 CFReleaseNull(results2
);
192 ok_status(SecItemCopyMatching(p
->query4
, &results2
),
193 "find non deleted internet password by persistent ref");
194 CFReleaseNull(results2
);
196 CFReleaseNull(update
);
198 CFReleaseNull(item2
);
199 CFReleaseNull(eighty
);
200 CFReleaseNull(pwdata
);
203 static void test_persistent2(struct test_persistent_s
*p
)
205 CFTypeRef results
= NULL
;
206 CFTypeRef results2
= NULL
;
208 ok_status(!SecItemCopyMatching(p
->query
, &results
),
209 "don't find internet password by attributes");
210 ok(!results
, "no results");
212 ok_status(!SecItemCopyMatching(p
->query2
, &results2
),
213 "don't find internet password by persistent ref anymore");
214 ok(!results2
, "no results");
217 ok_status(SecItemCopyMatching(p
->query4
, &results2
), "find non updated internet password by persistent ref");
218 skip("non updated internet password by persistent ref NOT FOUND!", 2, results2
);
219 ok(results2
, "non updated internet password not found");
220 CFStringRef server
= CFDictionaryGetValue(results2
, kSecAttrServer
);
221 ok(CFEqual(server
, CFSTR("zuigt.nl")), "verify second items attribute was not modified by update");
222 CFReleaseNull(results2
);
225 is_status(SecItemCopyMatching(p
->query3
, &results2
), errSecItemNotFound
,
226 "don't find deleted internet password by persistent ref");
227 CFReleaseNull(results2
);
228 ok_status(SecItemCopyMatching(p
->query4
, &results2
),
229 "find non deleted internet password by persistent ref");
230 CFReleaseNull(results2
);
232 ok_status(SecItemDelete(p
->query4
),"Deleted internet password by persistent ref");
235 CFRelease(p
->query2
);
236 CFRelease(p
->query3
);
237 CFRelease(p
->query4
);
238 CFReleaseNull(p
->persist
[0]);
239 CFReleaseNull(p
->persist
[1]);
242 static CFMutableDictionaryRef
test_create_lockdown_identity_query(void) {
243 CFMutableDictionaryRef query
= CFDictionaryCreateMutableForCFTypes(NULL
);
244 CFDictionaryAddValue(query
, kSecClass
, kSecClassGenericPassword
);
245 CFDictionaryAddValue(query
, kSecAttrAccount
, CFSTR("test-delete-me"));
246 CFDictionaryAddValue(query
, kSecAttrAccessGroup
, CFSTR("lockdown-identities"));
250 static CFMutableDictionaryRef
test_create_managedconfiguration_query(void) {
251 CFMutableDictionaryRef query
= CFDictionaryCreateMutableForCFTypes(NULL
);
252 CFDictionaryAddValue(query
, kSecClass
, kSecClassGenericPassword
);
253 CFDictionaryAddValue(query
, kSecAttrService
, CFSTR("com.apple.managedconfiguration"));
254 CFDictionaryAddValue(query
, kSecAttrAccount
, CFSTR("Public"));
255 CFDictionaryAddValue(query
, kSecAttrAccessGroup
, CFSTR("apple"));
259 static void test_add_lockdown_identity_items(void) {
260 CFMutableDictionaryRef query
= test_create_lockdown_identity_query();
261 const char *v_data
= "lockdown identity data (which should be a cert + key)";
262 CFDataRef pwdata
= CFDataCreate(NULL
, (UInt8
*)v_data
, strlen(v_data
));
263 CFDictionaryAddValue(query
, kSecValueData
, pwdata
);
264 ok_status(SecItemAdd(query
, NULL
), "test_add_lockdown_identity_items");
265 CFReleaseSafe(pwdata
);
266 CFReleaseSafe(query
);
269 static void test_remove_lockdown_identity_items(void) {
270 CFMutableDictionaryRef query
= test_create_lockdown_identity_query();
271 ok_status(SecItemDelete(query
), "test_remove_lockdown_identity_items");
272 CFReleaseSafe(query
);
275 static void test_no_find_lockdown_identity_item(void) {
276 CFMutableDictionaryRef query
= test_create_lockdown_identity_query();
277 is_status(SecItemCopyMatching(query
, NULL
), errSecItemNotFound
,
278 "test_no_find_lockdown_identity_item");
279 CFReleaseSafe(query
);
282 static CFMutableDictionaryRef
test_create_sysbound_query(void) {
283 CFMutableDictionaryRef query
= CFDictionaryCreateMutableForCFTypes(NULL
);
284 CFDictionaryAddValue(query
, kSecClass
, kSecClassGenericPassword
);
285 CFDictionaryAddValue(query
, kSecAttrAccount
, CFSTR("sysbound"));
286 CFDictionaryAddValue(query
, kSecAttrAccessGroup
, CFSTR("apple"));
290 static void test_add_sysbound_item(void) {
291 CFMutableDictionaryRef query
= test_create_sysbound_query();
292 int32_t val
= kSecSecAttrSysBoundPreserveDuringRestore
;
293 CFNumberRef num
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &val
);
294 CFDictionaryAddValue(query
, kSecAttrSysBound
, num
);
297 const char *v_data
= "sysbound identity data";
298 CFDataRef pwdata
= CFDataCreate(NULL
, (UInt8
*)v_data
, strlen(v_data
));
299 CFDictionaryAddValue(query
, kSecValueData
, pwdata
);
300 ok_status(SecItemAdd(query
, NULL
), "test_add_sysbound_item");
301 CFReleaseSafe(pwdata
);
302 CFReleaseSafe(query
);
305 static void test_remove_sysbound_item(void) {
306 CFMutableDictionaryRef query
= test_create_sysbound_query();
307 ok_status(SecItemDelete(query
), "test_remove_sysbound_item");
308 CFReleaseSafe(query
);
311 static void test_find_sysbound_item(OSStatus expectedCode
) {
312 CFMutableDictionaryRef query
= test_create_sysbound_query();
313 is_status(SecItemCopyMatching(query
, NULL
), expectedCode
,
314 "test_find_sysbound_item");
315 CFReleaseSafe(query
);
322 static CFMutableDictionaryRef
test_create_bt_query(bool sync
) {
323 CFMutableDictionaryRef query
= CFDictionaryCreateMutableForCFTypes(NULL
);
324 CFDictionaryAddValue(query
, kSecClass
, kSecClassGenericPassword
);
325 CFDictionaryAddValue(query
, kSecAttrAccessGroup
, CFSTR("com.apple.bluetooth"));
326 CFDictionaryAddValue(query
, kSecAttrSynchronizable
, sync
? kCFBooleanTrue
: kCFBooleanFalse
);
327 CFDictionarySetValue(query
, kSecAttrAccount
, sync
? CFSTR("sync") : CFSTR("non-sync"));
331 static void test_add_bt_items(const char *data
) {
332 CFMutableDictionaryRef query
= NULL
;
334 CFDataRef pwdata
= CFDataCreate(NULL
, (UInt8
*)data
, strlen(data
));
336 query
= test_create_bt_query(false);
337 (void)SecItemDelete(query
);
338 CFDictionarySetValue(query
, kSecValueData
, pwdata
);
339 ok_status(SecItemAdd(query
, NULL
), "test_add_bt_item(nonsync)");
340 CFReleaseSafe(query
);
342 query
= test_create_bt_query(true);
343 (void)SecItemDelete(query
);
344 CFDictionarySetValue(query
, kSecValueData
, pwdata
);
345 ok_status(SecItemAdd(query
, NULL
), "test_add_bt_item(sync)");
346 CFReleaseSafe(query
);
348 CFReleaseSafe(pwdata
);
351 static void test_find_bt_item(OSStatus expectedCode
, bool sync
, const char *data
) {
352 CFMutableDictionaryRef query
= test_create_bt_query(sync
);
353 CFDictionarySetValue(query
, kSecReturnData
, kCFBooleanTrue
);
354 CFDataRef pwdata
= NULL
;
355 is_status(SecItemCopyMatching(query
, (CFTypeRef
*)&pwdata
), expectedCode
,
356 "test_find_bt_item: %s", data
);
357 CFIndex len
= strlen(data
);
358 is(len
, CFDataGetLength(pwdata
), "length wrong(%s)", data
);
359 ok(memcmp(data
, CFDataGetBytePtr(pwdata
), len
) == 0, "length wrong(%s)", data
);
360 CFReleaseSafe(query
);
367 static void test_add_managedconfiguration_item(void) {
368 CFMutableDictionaryRef query
= test_create_managedconfiguration_query();
369 const char *v_data
= "public managedconfiguration password history data";
370 CFDataRef pwdata
= CFDataCreate(NULL
, (UInt8
*)v_data
, strlen(v_data
));
371 CFDictionaryAddValue(query
, kSecValueData
, pwdata
);
372 ok_status(SecItemAdd(query
, NULL
), "test_add_managedconfiguration_item");
373 CFReleaseSafe(pwdata
);
374 CFReleaseSafe(query
);
377 static void test_find_managedconfiguration_item(void) {
378 CFMutableDictionaryRef query
= test_create_managedconfiguration_query();
379 ok_status(SecItemCopyMatching(query
, NULL
), "test_find_managedconfiguration_item");
380 ok_status(SecItemDelete(query
), "test_find_managedconfiguration_item (deleted)");
381 CFReleaseSafe(query
);
385 #define DATA_ARG(x) (x) ? CFDataGetBytePtr((x)) : NULL, (x) ? (int)CFDataGetLength((x)) : 0
386 static CFDataRef
create_keybag(keybag_handle_t bag_type
, CFDataRef password
)
388 CFDataRef result
= NULL
;
392 keybag_handle_t handle
= bad_keybag_handle
;
393 kern_return_t bag_created
= aks_create_bag(DATA_ARG(password
), bag_type
, &handle
);
394 ok_status(bag_created
, "Bag should have been created");
395 require_noerr(bag_created
, out
);
397 kern_return_t bag_saved
= aks_save_bag(handle
, &bag
, &bagLen
);
398 ok_status(bag_saved
, "Bag should have been saved");
399 require_noerr(bag_saved
, out
);
401 result
= CFDataCreate(kCFAllocatorDefault
, bag
, bagLen
);
402 isnt(result
, NULL
, "Result should not be null");
408 /* Test low level keychain migration from device to device interface. */
409 static void tests(void)
412 CFMutableDictionaryRef lock_down_query
= test_create_lockdown_identity_query();
413 (void)SecItemDelete(lock_down_query
);
414 CFReleaseNull(lock_down_query
);
417 CFMutableDictionaryRef sysbound_query
= test_create_sysbound_query();
418 (void)SecItemDelete(sysbound_query
);
419 CFReleaseNull(sysbound_query
);
422 CFMutableDictionaryRef managed_configuration_query
= test_create_managedconfiguration_query();
423 (void)SecItemDelete(managed_configuration_query
);
424 CFReleaseNull(managed_configuration_query
);
428 CFNumberRef eighty
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &v_eighty
);
429 const char *v_data
= "test";
430 CFDataRef pwdata
= CFDataCreate(NULL
, (UInt8
*)v_data
, strlen(v_data
));
431 CFMutableDictionaryRef query
= CFDictionaryCreateMutableForCFTypes(NULL
);
432 CFDictionaryAddValue(query
, kSecClass
, kSecClassInternetPassword
);
433 CFDictionaryAddValue(query
, kSecAttrServer
, CFSTR("members.spamcop.net"));
434 CFDictionaryAddValue(query
, kSecAttrAccount
, CFSTR("smith"));
435 CFDictionaryAddValue(query
, kSecAttrPort
, eighty
);
436 CFDictionaryAddValue(query
, kSecAttrProtocol
, kSecAttrProtocolHTTP
);
437 CFDictionaryAddValue(query
, kSecAttrAuthenticationType
, kSecAttrAuthenticationTypeDefault
);
438 CFDictionaryAddValue(query
, kSecValueData
, pwdata
);
439 // NUKE anything we might have left around from a previous test run so we don't crash.
440 (void)SecItemDelete(query
);
442 ok_status(SecItemAdd(query
, NULL
), "add internet password");
443 is_status(SecItemAdd(query
, NULL
), errSecDuplicateItem
,
444 "add internet password again");
446 ok_status(SecItemCopyMatching(query
, NULL
), "Found the item we added");
448 struct test_persistent_s p
= {};
451 CFDataRef backup
= NULL
, keybag
= NULL
, password
= NULL
;
453 test_add_lockdown_identity_items();
454 test_add_sysbound_item();
455 test_add_bt_items("kaka1");
458 keybag
= create_keybag(kAppleKeyStoreBackupBag
, password
);
460 keybag
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
463 ok(backup
= _SecKeychainCopyBackup(keybag
, password
),
464 "_SecKeychainCopyBackup");
466 test_add_managedconfiguration_item();
467 test_remove_lockdown_identity_items();
468 test_remove_sysbound_item();
470 test_add_bt_items("kaka2");
472 ok_status(_SecKeychainRestoreBackup(backup
, keybag
, password
),
473 "_SecKeychainRestoreBackup");
474 CFReleaseSafe(backup
);
476 test_no_find_lockdown_identity_item();
477 test_find_sysbound_item(errSecItemNotFound
);
478 test_find_managedconfiguration_item();
480 * Check that the kaka1 entry was "overwritten"
482 test_find_bt_item(errSecSuccess
, true, "kaka2");
483 test_find_bt_item(errSecSuccess
, false, "kaka1");
485 ok_status(SecItemCopyMatching(query
, NULL
),
486 "Found the item we added after restore");
488 test_persistent2(&p
);
491 CFReleaseNull(keybag
);
492 keybag
= create_keybag(kAppleKeyStoreOTABackupBag
, password
);
495 ok(backup
= _SecKeychainCopyBackup(keybag
, password
),
496 "_SecKeychainCopyBackup");
497 ok_status(_SecKeychainRestoreBackup(backup
, keybag
, password
),
498 "_SecKeychainRestoreBackup");
499 ok_status(SecItemCopyMatching(query
, NULL
),
500 "Found the item we added after restore");
501 CFReleaseNull(backup
);
503 // force tombstone to be added, since it's not the default behavior per rdar://14680869
504 CFDictionaryAddValue(query
, kSecUseTombstones
, kCFBooleanTrue
);
506 ok_status(SecItemDelete(query
), "Deleted item we added");
509 CFReleaseNull(keybag
);
510 keybag
= create_keybag(kAppleKeyStoreOTABackupBag
/* use truthiness bag once it's there */, password
);
514 CFDictionaryAddValue(query
, kSecAttrSynchronizable
, kCFBooleanTrue
);
515 ok_status(SecItemAdd(query
, NULL
), "add internet password");
517 // and non-syncable item
518 test_add_managedconfiguration_item();
520 CFDictionaryRef syncableBackup
= NULL
;
522 CFErrorRef error
= NULL
;
523 CFDictionaryRef scratch
= NULL
;
525 skip("skipping syncable backup tests", 7,
526 ok_status(_SecKeychainBackupSyncable(keybag
, password
, NULL
, &syncableBackup
), "export items"));
528 // TODO: add item, call SecServerCopyTruthInTheCloud again
530 // CFShow(syncableBackup);
533 skip("skipping syncable backup tests", 6,
534 ok_status(SecItemCopyMatching(query
, NULL
), "find item we are about to destroy"));
536 skip("skipping syncable backup tests", 5,
537 ok_status(SecItemDelete(query
), "delete item we backed up"));
539 // ensure we added a tombstone
540 CFDictionaryAddValue(query
, kSecAttrTombstone
, kCFBooleanTrue
);
541 skip("skipping syncable backup tests", 4,
542 ok_status(SecItemCopyMatching(query
, NULL
), "find tombstone for item we deleted"));
543 CFDictionaryRemoveValue(query
, kSecAttrTombstone
);
545 test_find_managedconfiguration_item(); // <- 2 tests here
547 // TODO: add a different new item - delete what's not in the syncableBackup?
549 // Do another backup after some changes
550 skip("skipping syncable backup tests", 1,
551 ok_status(_SecKeychainBackupSyncable(keybag
, password
, syncableBackup
, &scratch
), "export items after changes"));
553 skip("skipping syncable backup tests", 0,
554 ok_status(_SecKeychainRestoreSyncable(keybag
, password
, syncableBackup
), "import items"));
556 CFReleaseNull(scratch
);
557 CFReleaseNull(error
);
559 // non-syncable item should (still) be gone -> add should work
560 test_add_managedconfiguration_item();
561 test_find_managedconfiguration_item();
563 // syncable item should have not been restored, because the tombstone was newer than the item in the backup -> copy matching should fail
564 is_status(errSecItemNotFound
, SecItemCopyMatching(query
, NULL
),
565 "find restored item");
566 is_status(errSecItemNotFound
, SecItemDelete(query
), "delete restored item");
568 CFReleaseSafe(syncableBackup
);
569 CFReleaseSafe(keybag
);
570 CFReleaseSafe(eighty
);
571 CFReleaseSafe(pwdata
);
572 CFReleaseSafe(query
);
575 int si_33_keychain_backup(int argc
, char *const *argv
)