static int do_keychain_import(const char *backupPath, const char *keybagPath, const char *passwordString) { CFDataRef backup=NULL; CFDataRef keybag=NULL; CFDataRef password=NULL; bool ok=false; if(passwordString) { require(password = CFDataCreate(NULL, (UInt8 *)passwordString, strlen(passwordString)), out); } require(keybag=copyFileContents(keybagPath), out); require(backup=copyFileContents(backupPath), out); ok=_SecKeychainRestoreBackup(backup, keybag, password); out: CFReleaseSafe(backup); CFReleaseSafe(keybag); CFReleaseSafe(password); return ok?0:1; }
/* Test low level keychain migration from device to device interface. */ static void tests(void) { int v_eighty = 80; CFNumberRef eighty = CFNumberCreate(NULL, kCFNumberSInt32Type, &v_eighty); const char *v_data = "test"; CFDataRef pwdata = CFDataCreate(NULL, (UInt8 *)v_data, strlen(v_data)); CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); CFDictionaryAddValue(query, kSecClass, kSecClassInternetPassword); CFDictionaryAddValue(query, kSecAttrServer, CFSTR("members.spamcop.net")); CFDictionaryAddValue(query, kSecAttrAccount, CFSTR("smith")); CFDictionaryAddValue(query, kSecAttrPort, eighty); CFDictionaryAddValue(query, kSecAttrProtocol, kSecAttrProtocolHTTP); CFDictionaryAddValue(query, kSecAttrAuthenticationType, kSecAttrAuthenticationTypeDefault); CFDictionaryAddValue(query, kSecValueData, pwdata); // NUKE anything we might have left around from a previous test run so we don't crash. SecItemDelete(query); ok_status(SecItemAdd(query, NULL), "add internet password"); is_status(SecItemAdd(query, NULL), errSecDuplicateItem, "add internet password again"); ok_status(SecItemCopyMatching(query, NULL), "Found the item we added"); struct test_persistent_s p = {}; test_persistent(&p); CFDataRef backup = NULL, keybag = NULL, password = NULL; test_add_lockdown_identity_items(); #if USE_KEYSTORE keybag = create_keybag(kAppleKeyStoreBackupBag, password); #else keybag = CFDataCreate(kCFAllocatorDefault, NULL, 0); #endif ok(backup = _SecKeychainCopyBackup(keybag, password), "_SecKeychainCopyBackup"); test_add_managedconfiguration_item(); test_remove_lockdown_identity_items(); ok_status(_SecKeychainRestoreBackup(backup, keybag, password), "_SecKeychainRestoreBackup"); CFReleaseSafe(backup); test_no_find_lockdown_identity_item(); test_find_managedconfiguration_item(); ok_status(SecItemCopyMatching(query, NULL), "Found the item we added after restore"); test_persistent2(&p); #if USE_KEYSTORE CFReleaseNull(keybag); keybag = create_keybag(kAppleKeyStoreOTABackupBag, password); #endif ok(backup = _SecKeychainCopyBackup(keybag, password), "_SecKeychainCopyBackup"); ok_status(_SecKeychainRestoreBackup(backup, keybag, password), "_SecKeychainRestoreBackup"); ok_status(SecItemCopyMatching(query, NULL), "Found the item we added after restore"); CFReleaseNull(backup); // force tombstone to be added, since it's not the default behavior per rdar://14680869 CFDictionaryAddValue(query, kSecUseTombstones, kCFBooleanTrue); ok_status(SecItemDelete(query), "Deleted item we added"); #if USE_KEYSTORE CFReleaseNull(keybag); keybag = create_keybag(kAppleKeyStoreOTABackupBag /* use truthiness bag once it's there */, password); #endif // add syncable item CFDictionaryAddValue(query, kSecAttrSynchronizable, kCFBooleanTrue); ok_status(SecItemAdd(query, NULL), "add internet password"); // and non-syncable item test_add_managedconfiguration_item(); CFDictionaryRef syncableBackup = NULL; CFErrorRef error = NULL; CFDictionaryRef scratch = NULL; SKIP: { skip("skipping syncable backup tests", 7, ok_status(_SecKeychainBackupSyncable(keybag, password, NULL, &syncableBackup), "export items")); // TODO: add item, call SecServerCopyTruthInTheCloud again // CFShow(syncableBackup); // find and delete skip("skipping syncable backup tests", 6, ok_status(SecItemCopyMatching(query, NULL), "find item we are about to destroy")); skip("skipping syncable backup tests", 5, ok_status(SecItemDelete(query), "delete item we backed up")); // ensure we added a tombstone CFDictionaryAddValue(query, kSecAttrTombstone, kCFBooleanTrue); skip("skipping syncable backup tests", 4, ok_status(SecItemCopyMatching(query, NULL), "find tombstone for item we deleted")); CFDictionaryRemoveValue(query, kSecAttrTombstone); test_find_managedconfiguration_item(); // <- 2 tests here // TODO: add a different new item - delete what's not in the syncableBackup? // Do another backup after some changes skip("skipping syncable backup tests", 1, ok_status(_SecKeychainBackupSyncable(keybag, password, syncableBackup, &scratch), "export items after changes")); skip("skipping syncable backup tests", 0, ok_status(_SecKeychainRestoreSyncable(keybag, password, syncableBackup), "import items")); } CFReleaseNull(scratch); CFReleaseNull(error); // non-syncable item should (still) be gone -> add should work test_add_managedconfiguration_item(); test_find_managedconfiguration_item(); // syncable item should have not been restored, because the tombstone was newer than the item in the backup -> copy matching should fail is_status(errSecItemNotFound, SecItemCopyMatching(query, NULL), "find restored item"); is_status(errSecItemNotFound, SecItemDelete(query), "delete restored item"); CFReleaseSafe(syncableBackup); CFReleaseSafe(keybag); CFReleaseSafe(eighty); CFReleaseSafe(pwdata); CFReleaseSafe(query); }