SOSTransportMessageKVS.c [plain text]
#include <Security/SecureObjectSync/SOSTransport.h>
#include <Security/SecureObjectSync/SOSTransportMessage.h>
#include <Security/SecureObjectSync/SOSTransportMessageKVS.h>
#include <Security/SecureObjectSync/SOSKVSKeys.h>
#include <Security/SecureObjectSync/SOSAccountPriv.h>
#include <utilities/SecCFWrappers.h>
#include <SOSInternal.h>
#include <AssertMacros.h>
#include <SOSCloudKeychainClient.h>
struct __OpaqueSOSTransportMessageKVS {
struct __OpaqueSOSTransportMessage m;
CFMutableDictionaryRef pending_changes;
};
static bool sendToPeer(SOSTransportMessageRef transport, CFStringRef circleName, CFStringRef peerID, CFDataRef message, CFErrorRef *error);
static bool syncWithPeers(SOSTransportMessageRef transport, CFDictionaryRef circleToPeerIDs, CFErrorRef *error);
static bool sendMessages(SOSTransportMessageRef transport, CFDictionaryRef circleToPeersToMessage, CFErrorRef *error);
static bool cleanupAfterPeer(SOSTransportMessageRef transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error);
static void destroy(SOSTransportMessageRef transport);
static inline CFIndex getTransportType(SOSTransportMessageRef transport, CFErrorRef *error);
static CF_RETURNS_RETAINED
CFDictionaryRef handleMessages(SOSTransportMessageRef transport, CFMutableDictionaryRef circle_peer_messages_table, CFErrorRef *error);
static bool flushChanges(SOSTransportMessageRef transport, CFErrorRef *error);
SOSTransportMessageKVSRef SOSTransportMessageKVSCreate(SOSAccountRef account, CFStringRef circleName, CFErrorRef *error){
SOSTransportMessageKVSRef tkvs = (SOSTransportMessageKVSRef) SOSTransportMessageCreateForSubclass(sizeof(struct __OpaqueSOSTransportMessageKVS) - sizeof(CFRuntimeBase), account, circleName, error);
if (tkvs) {
tkvs->m.sendMessages = sendMessages;
tkvs->m.syncWithPeers = syncWithPeers;
tkvs->m.flushChanges = flushChanges;
tkvs->m.cleanupAfterPeerMessages = cleanupAfterPeer;
tkvs->m.destroy = destroy;
tkvs->m.handleMessages = handleMessages;
tkvs->m.getTransportType = getTransportType;
tkvs->pending_changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
SOSRegisterTransportMessage((SOSTransportMessageRef)tkvs);
}
return tkvs;
}
bool SOSTransportMessageKVSAppendKeyInterest(SOSTransportMessageKVSRef transport, CFMutableArrayRef alwaysKeys, CFMutableArrayRef afterFirstUnlockKeys, CFMutableArrayRef unlockedKeys, CFErrorRef *localError){
SOSEngineRef engine = SOSTransportMessageGetEngine((SOSTransportMessageRef)transport);
require_quiet(engine, fail);
CFArrayRef peerInfos = SOSAccountCopyPeersToListenTo(SOSTransportMessageGetAccount((SOSTransportMessageRef) transport), localError);
if(peerInfos){
CFArrayForEach(peerInfos, ^(const void *value) {
SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
CFStringRef peerID = SOSPeerInfoGetPeerID(peer);
CFStringRef peerMessage = SOSMessageKeyCreateFromPeerToTransport((SOSTransportMessageRef)transport, peerID);
if(peerMessage != NULL)
CFArrayAppendValue(unlockedKeys, peerMessage);
CFReleaseNull(peerMessage);
});
CFReleaseNull(peerInfos);
}
return true;
fail:
return false;
}
static void destroy(SOSTransportMessageRef transport){
SOSTransportMessageKVSRef tkvs = (SOSTransportMessageKVSRef)transport;
CFReleaseNull(tkvs->pending_changes);
SOSUnregisterTransportMessage((SOSTransportMessageRef)tkvs);
}
static inline CFIndex getTransportType(SOSTransportMessageRef transport, CFErrorRef *error){
return kKVS;
}
static bool SOSTransportMessageKVSUpdateKVS(SOSTransportMessageKVSRef transport, CFDictionaryRef changes, CFErrorRef *error){
CloudKeychainReplyBlock log_error = ^(CFDictionaryRef returnedValues __unused, CFErrorRef error) {
if (error) {
secerror("Error putting: %@", error);
CFReleaseSafe(error);
}
};
SOSCloudKeychainPutObjectsInCloud(changes, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), log_error);
return true;
}
static bool SOSTransportMessageKVSSendPendingChanges(SOSTransportMessageKVSRef transport, CFErrorRef *error) {
CFErrorRef changeError = NULL;
if (transport->pending_changes == NULL || CFDictionaryGetCount(transport->pending_changes) == 0) {
CFReleaseNull(transport->pending_changes);
return true;
}
SOSAccountRef account = SOSTransportMessageGetAccount((SOSTransportMessageRef)transport);
CFTypeRef dsid = SOSAccountGetValue(account, kSOSDSIDKey, error);
if(dsid == NULL)
dsid = kCFNull;
CFDictionaryAddValue(transport->pending_changes, kSOSKVSRequiredKey, dsid);
bool success = SOSTransportMessageKVSUpdateKVS(transport, transport->pending_changes, &changeError);
if (success) {
CFDictionaryRemoveAllValues(transport->pending_changes);
} else {
SOSCreateErrorWithFormat(kSOSErrorSendFailure, changeError, error, NULL,
CFSTR("Send changes block failed [%@]"), transport->pending_changes);
}
return success;
}
static void SOSTransportMessageKVSAddToPendingChanges(SOSTransportMessageKVSRef transport, CFStringRef message_key, CFDataRef message_data){
if (transport->pending_changes == NULL) {
transport->pending_changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
}
if (message_data == NULL) {
CFDictionarySetValue(transport->pending_changes, message_key, kCFNull);
} else {
CFDictionarySetValue(transport->pending_changes, message_key, message_data);
}
}
static bool SOSTransportMessageKVSCleanupAfterPeerMessages(SOSTransportMessageKVSRef transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error)
{
CFArrayRef enginePeers = SOSEngineGetPeerIDs(SOSTransportMessageGetEngine((SOSTransportMessageRef)transport));
__block SOSAccountRef account = SOSTransportMessageGetAccount((SOSTransportMessageRef)transport);
CFDictionaryForEach(circle_to_peer_ids, ^(const void *key, const void *value) {
if (isString(key) && isArray(value)) {
CFStringRef circle_name = (CFStringRef) key;
CFArrayRef peers_to_cleanup_after = (CFArrayRef) value;
CFArrayForEach(peers_to_cleanup_after, ^(const void *value) {
if (isString(value)) {
CFStringRef cleanup_id = (CFStringRef) value;
if (enginePeers) CFArrayForEach(enginePeers, ^(const void *value) {
if (isString(value)) {
CFStringRef in_circle_id = (CFStringRef) value;
CFStringRef kvsKey = SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name, cleanup_id, in_circle_id);
SOSTransportMessageKVSAddToPendingChanges(transport, kvsKey, NULL);
CFReleaseSafe(kvsKey);
CFStringRef lastCirclePushedKey = SOSLastCirclePushedKeyCreateWithCircleNameAndPeerID(circle_name, cleanup_id);
SOSTransportMessageKVSAddToPendingChanges(transport, lastCirclePushedKey, NULL);
CFReleaseSafe(lastCirclePushedKey);
CFStringRef lastKeyParameterPushedKey = SOSLastKeyParametersPushedKeyCreateWithPeerID(cleanup_id);
SOSTransportMessageKVSAddToPendingChanges(transport, lastKeyParameterPushedKey, NULL);
CFReleaseSafe(lastKeyParameterPushedKey);
CFStringRef lastCirclePushedWithAccountGestaltKey = SOSLastCirclePushedKeyCreateWithAccountGestalt(account);
SOSTransportMessageKVSAddToPendingChanges(transport, lastCirclePushedKey, NULL);
CFReleaseSafe(lastCirclePushedWithAccountGestaltKey);
CFStringRef lastKeyParameterWithAccountGestaltKey = SOSLastKeyParametersPushedKeyCreateWithAccountGestalt(account);
SOSTransportMessageKVSAddToPendingChanges(transport, lastCirclePushedKey, NULL);
CFReleaseSafe(lastKeyParameterWithAccountGestaltKey);
kvsKey = SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name, in_circle_id, cleanup_id);
SOSTransportMessageKVSAddToPendingChanges(transport, kvsKey, NULL);
CFReleaseSafe(kvsKey);
}
});
}
});
}
});
return SOSTransportMessageFlushChanges((SOSTransportMessageRef)transport, error);
}
static CF_RETURNS_RETAINED
CFDictionaryRef handleMessages(SOSTransportMessageRef transport, CFMutableDictionaryRef circle_peer_messages_table, CFErrorRef *error) {
CFMutableDictionaryRef handled = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
CFDictionaryRef peerToMessage = CFDictionaryGetValue(circle_peer_messages_table, transport->circleName);
CFMutableArrayRef handled_peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
if(peerToMessage){
CFDictionaryForEach(peerToMessage, ^(const void *key, const void *value) {
CFStringRef peer_id = asString(key, NULL);
CFDataRef peer_message = asData(value, NULL);
CFErrorRef localError = NULL;
if (peer_id && peer_message && SOSTransportMessageHandlePeerMessage(transport, peer_id, peer_message, &localError)) {
CFArrayAppendValue(handled_peers, key);
} else {
secnotice("transport", "%@ KVSTransport handle message failed: %@", peer_id, localError);
}
CFReleaseNull(localError);
});
}
CFDictionaryAddValue(handled, transport->circleName, handled_peers);
CFReleaseNull(handled_peers);
return handled;
}
static bool sendToPeer(SOSTransportMessageRef transport, CFStringRef circleName, CFStringRef peerID, CFDataRef message, CFErrorRef *error) {
SOSTransportMessageKVSRef kvsTransport = (SOSTransportMessageKVSRef) transport;
bool result = true;
SOSAccountRef account = SOSTransportMessageGetAccount((SOSTransportMessageRef)transport);
CFTypeRef dsid = SOSAccountGetValue(account, kSOSDSIDKey, error);
if(dsid == NULL)
dsid = kCFNull;
CFStringRef message_to_peer_key = SOSMessageKeyCreateFromTransportToPeer((SOSTransportMessageRef)kvsTransport, peerID);
CFDictionaryRef a_message_to_a_peer = CFDictionaryCreateForCFTypes(NULL, message_to_peer_key, message, kSOSKVSRequiredKey, dsid, NULL);
if (!SOSTransportMessageKVSUpdateKVS(kvsTransport, a_message_to_a_peer, error)) {
secerror("Sync with peers failed to send to %@ [%@], %@", peerID, a_message_to_a_peer, *error);
result = false;
}
CFReleaseNull(a_message_to_a_peer);
CFReleaseNull(message_to_peer_key);
return result;
}
static bool syncWithPeers(SOSTransportMessageRef transport, CFDictionaryRef circleToPeerIDs, CFErrorRef *error) {
__block bool result = true;
CFDictionaryForEach(circleToPeerIDs, ^(const void *key, const void *value) {
if (isString(key) && isArray(value)) {
CFStringRef circleName = (CFStringRef) key;
CFArrayForEach(value, ^(const void *value) {
if (isString(value)) {
CFStringRef peerID = (CFStringRef) value;
result &= SOSTransportMessageSendMessageIfNeeded(transport, circleName, peerID, error);
}
});
}
});
return result;
}
static bool sendMessages(SOSTransportMessageRef transport, CFDictionaryRef circleToPeersToMessage, CFErrorRef *error) {
__block bool result = true;
CFDictionaryForEach(circleToPeersToMessage, ^(const void *key, const void *value) {
if (isString(key) && isDictionary(value)) {
CFStringRef circleName = (CFStringRef) key;
CFDictionaryForEach(value, ^(const void *key, const void *value) {
if (isString(key) && isData(value)) {
CFStringRef peerID = (CFStringRef) key;
CFDataRef message = (CFDataRef) value;
bool rx = sendToPeer(transport, circleName, peerID, message, error);
result &= rx;
}
});
}
});
return true;
}
static bool flushChanges(SOSTransportMessageRef transport, CFErrorRef *error)
{
return SOSTransportMessageKVSSendPendingChanges((SOSTransportMessageKVSRef) transport, error);
}
static bool cleanupAfterPeer(SOSTransportMessageRef transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error)
{
return SOSTransportMessageKVSCleanupAfterPeerMessages((SOSTransportMessageKVSRef) transport, circle_to_peer_ids, error);
}