SecItemBackupServer.c [plain text]
#include "keychain/securityd/SecItemBackupServer.h"
#include "keychain/securityd/SecItemServer.h"
#include "keychain/SecureObjectSync/SOSEnginePriv.h"
#include "keychain/SecureObjectSync/SOSPeer.h"
#include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h>
#include <Security/SecureObjectSync/SOSViews.h>
#include <unistd.h>
#include "keychain/securityd/SecDbItem.h"
#include <utilities/der_plist.h>
static bool withDataSourceAndEngine(CFErrorRef *error, void (^action)(SOSDataSourceRef ds, SOSEngineRef engine)) {
bool ok = false;
SOSDataSourceFactoryRef dsf = SecItemDataSourceFactoryGetDefault();
SOSDataSourceRef ds = SOSDataSourceFactoryCreateDataSource(dsf, kSecAttrAccessibleWhenUnlocked, error);
if (ds) {
SOSEngineRef engine = SOSDataSourceGetSharedEngine(ds, error);
if (engine) {
action(ds, engine);
ok = true;
}
ok &= SOSDataSourceRelease(ds, error);
}
return ok;
}
int SecServerItemBackupHandoffFD(CFStringRef backupName, CFErrorRef *error) {
__block int fd = -1;
if (!withDataSourceAndEngine(error, ^(SOSDataSourceRef ds, SOSEngineRef engine) {
SOSEngineForPeerID(engine, backupName, error, ^(SOSTransactionRef txn, SOSPeerRef peer) {
fd = SOSPeerHandoffFD(peer, error);
});
}) && fd >= 0) {
close(fd);
fd = -1;
}
return fd;
}
bool SecServerItemBackupSetConfirmedManifest(CFStringRef backupName, CFDataRef keybagDigest, CFDataRef manifestData, CFErrorRef *error) {
__block bool ok = true;
ok &= withDataSourceAndEngine(error, ^(SOSDataSourceRef ds, SOSEngineRef engine) {
ok = SOSEngineSetPeerConfirmedManifest(engine, backupName, keybagDigest, manifestData, error);
});
return ok;
}
CFArrayRef SecServerItemBackupCopyNames(CFErrorRef *error) {
__block CFArrayRef names = NULL;
if (!withDataSourceAndEngine(error, ^(SOSDataSourceRef ds, SOSEngineRef engine) {
names = SOSEngineCopyBackupPeerNames(engine, error);
})) {
CFReleaseNull(names);
}
return names;
}
static bool SOSDataSourceWithBackup(SOSDataSourceRef ds, CFDataRef backup, keybag_handle_t bag_handle, CFErrorRef *error, void(^with)(SOSObjectRef item)) {
__block bool ok = true;
CFPropertyListRef plist = CFPropertyListCreateWithDERData(kCFAllocatorDefault, backup, kCFPropertyListImmutable, NULL, error);
CFDictionaryRef bdict = asDictionary(plist, error);
ok = (bdict != NULL);
if (ok) CFDictionaryForEach(bdict, ^(const void *key, const void *value) {
CFStringRef className = asString(key, error);
if (className) {
const SecDbClass *cls = kc_class_with_name(className);
if (cls) {
CFArrayRef items = asArray(value, error);
CFDataRef edata;
if (items) CFArrayForEachC(items, edata) {
SOSObjectRef item = (SOSObjectRef)SecDbItemCreateWithEncryptedData(kCFAllocatorDefault, cls, edata, bag_handle, error);
if (item) {
with(item);
CFRelease(item);
} else {
ok = false;
}
} else {
ok = false;
}
} else {
ok &= SecError(errSecDecode, error, CFSTR("bad class %@ in backup"), className);
}
} else {
ok = false;
}
});
CFReleaseSafe(plist);
return ok;
}
bool SecServerItemBackupRestore(CFStringRef backupName, CFStringRef peerID, CFDataRef keybag, CFDataRef secret, CFDataRef backup, CFErrorRef *error) {
__block bool ok = false; CFDataRef aksKeybag = NULL;
CFMutableSetRef viewSet = NULL;
SOSBackupSliceKeyBagRef backupSliceKeyBag = NULL;
keybag_handle_t bag_handle = bad_keybag_handle;
require(asData(secret, error), xit);
require(backupSliceKeyBag = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, keybag, error), xit);
if (peerID) {
bag_handle = SOSBSKBLoadAndUnlockWithPeerIDAndSecret(backupSliceKeyBag, peerID, secret, error);
} else {
if (SOSBSKBIsDirect(backupSliceKeyBag)) {
bag_handle = SOSBSKBLoadAndUnlockWithDirectSecret(backupSliceKeyBag, secret, error);
} else {
bag_handle = SOSBSKBLoadAndUnlockWithWrappingSecret(backupSliceKeyBag, secret, error);
}
}
require(bag_handle != bad_keybag_handle, xit);
ok = true; ok &= withDataSourceAndEngine(error, ^(SOSDataSourceRef ds, SOSEngineRef engine) {
ok &= SOSDataSourceWith(ds, error, ^(SOSTransactionRef txn, bool *commit) {
ok &= SOSDataSourceWithBackup(ds, backup, bag_handle, error, ^(SOSObjectRef item) {
SOSObjectRef mergedItem = NULL;
if (SOSDataSourceMergeObject(ds, txn, item, &mergedItem, error)) {
CFReleaseSafe(mergedItem);
}
});
});
});
xit:
if (bag_handle != bad_keybag_handle)
ok &= ks_close_keybag(bag_handle, error);
CFReleaseSafe(backupSliceKeyBag);
CFReleaseSafe(aksKeybag);
CFReleaseSafe(viewSet);
return ok;
}