1#include <SecureObjectSync/SOSTransport.h> 2#include <SecureObjectSync/SOSTransportMessage.h> 3#include <SecureObjectSync/SOSTransportMessageKVS.h> 4#include <SecureObjectSync/SOSKVSKeys.h> 5 6#include <utilities/SecCFWrappers.h> 7#include <SOSInternal.h> 8#include <AssertMacros.h> 9#include <SOSCloudKeychainClient.h> 10 11struct __OpaqueSOSTransportMessageKVS { 12 struct __OpaqueSOSTransportMessage m; 13 14 CFStringRef circleName; 15 CFMutableDictionaryRef pending_changes; 16 17}; 18 19// 20// V-table implementation forward declarations 21// 22static bool sendToPeer(SOSTransportMessageRef transport, CFStringRef circleName, CFStringRef peerID, CFDataRef message, CFErrorRef *error); 23static bool syncWithPeers(SOSTransportMessageRef transport, CFDictionaryRef circleToPeerIDs, CFErrorRef *error); 24static bool sendMessages(SOSTransportMessageRef transport, CFDictionaryRef circleToPeersToMessage, CFErrorRef *error); 25static bool cleanupAfterPeer(SOSTransportMessageRef transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error); 26static void destroy(SOSTransportMessageRef transport); 27static CF_RETURNS_RETAINED 28CFDictionaryRef handleMessages(SOSTransportMessageRef transport, CFMutableDictionaryRef circle_peer_messages_table, CFErrorRef *error); 29 30static bool flushChanges(SOSTransportMessageRef transport, CFErrorRef *error); 31 32 33CFStringRef SOSTransportMessageKVSGetCircleName(SOSTransportMessageKVSRef transport){ 34 return transport->circleName; 35} 36 37SOSTransportMessageKVSRef SOSTransportMessageKVSCreate(SOSAccountRef account, CFStringRef circleName, CFErrorRef *error){ 38 SOSTransportMessageKVSRef tkvs = (SOSTransportMessageKVSRef) SOSTransportMessageCreateForSubclass(sizeof(struct __OpaqueSOSTransportMessageKVS) - sizeof(CFRuntimeBase), account, circleName, error); 39 40 if (tkvs) { 41 // Fill in vtable: 42 tkvs->m.sendMessages = sendMessages; 43 tkvs->m.syncWithPeers = syncWithPeers; 44 tkvs->m.flushChanges = flushChanges; 45 tkvs->m.cleanupAfterPeerMessages = cleanupAfterPeer; 46 tkvs->m.destroy = destroy; 47 tkvs->m.handleMessages = handleMessages; 48 // Initialize ourselves 49 tkvs->pending_changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 50 tkvs->circleName = CFRetainSafe(circleName); 51 SOSRegisterTransportMessage((SOSTransportMessageRef)tkvs); 52 } 53 54 return tkvs; 55} 56bool SOSTransportMessageKVSAppendKeyInterest(SOSTransportMessageKVSRef transport, CFMutableArrayRef alwaysKeys, CFMutableArrayRef afterFirstUnlockKeys, CFMutableArrayRef unlockedKeys, CFErrorRef *localError){ 57 SOSEngineRef engine = SOSTransportMessageGetEngine((SOSTransportMessageRef)transport); 58 require_quiet(engine, fail); 59 CFArrayRef peer_ids = SOSEngineGetPeerIDs(engine); 60 if(peer_ids){ 61 CFArrayForEach(peer_ids, ^(const void *value) { 62 CFStringRef peerMessage = SOSMessageKeyCreateFromPeerToTransport(transport, value); 63 CFArrayAppendValue(unlockedKeys, peerMessage); 64 CFReleaseNull(peerMessage); 65 }); 66 } 67 return true; 68fail: 69 return false; 70} 71static void destroy(SOSTransportMessageRef transport){ 72 SOSTransportMessageKVSRef tkvs = (SOSTransportMessageKVSRef)transport; 73 CFReleaseNull(tkvs->circleName); 74 CFReleaseNull(tkvs->pending_changes); 75 SOSUnregisterTransportMessage((SOSTransportMessageRef)tkvs); 76 77} 78 79static bool SOSTransportMessageKVSUpdateKVS(SOSTransportMessageKVSRef transport, CFDictionaryRef changes, CFErrorRef *error){ 80 CloudKeychainReplyBlock log_error = ^(CFDictionaryRef returnedValues __unused, CFErrorRef error) { 81 if (error) { 82 secerror("Error putting: %@", error); 83 CFReleaseSafe(error); 84 } 85 }; 86 87 SOSCloudKeychainPutObjectsInCloud(changes, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), log_error); 88 return true; 89} 90 91static bool SOSTransportMessageKVSSendPendingChanges(SOSTransportMessageKVSRef transport, CFErrorRef *error) { 92 CFErrorRef changeError = NULL; 93 94 if (transport->pending_changes == NULL || CFDictionaryGetCount(transport->pending_changes) == 0) { 95 CFReleaseNull(transport->pending_changes); 96 return true; 97 } 98 99 bool success = SOSTransportMessageKVSUpdateKVS(transport, transport->pending_changes, &changeError); 100 if (success) { 101 CFDictionaryRemoveAllValues(transport->pending_changes); 102 } else { 103 SOSCreateErrorWithFormat(kSOSErrorSendFailure, changeError, error, NULL, 104 CFSTR("Send changes block failed [%@]"), transport->pending_changes); 105 } 106 107 return success; 108} 109 110static void SOSTransportMessageKVSAddToPendingChanges(SOSTransportMessageKVSRef transport, CFStringRef message_key, CFDataRef message_data){ 111 if (transport->pending_changes == NULL) { 112 transport->pending_changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 113 } 114 if (message_data == NULL) { 115 CFDictionarySetValue(transport->pending_changes, message_key, kCFNull); 116 } else { 117 CFDictionarySetValue(transport->pending_changes, message_key, message_data); 118 } 119} 120 121static bool SOSTransportMessageKVSCleanupAfterPeerMessages(SOSTransportMessageKVSRef transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error) 122{ 123 CFArrayRef enginePeers = SOSEngineGetPeerIDs(SOSTransportMessageGetEngine((SOSTransportMessageRef)transport)); 124 125 CFDictionaryForEach(circle_to_peer_ids, ^(const void *key, const void *value) { 126 if (isString(key) && isArray(value)) { 127 CFStringRef circle_name = (CFStringRef) key; 128 CFArrayRef peers_to_cleanup_after = (CFArrayRef) value; 129 130 CFArrayForEach(peers_to_cleanup_after, ^(const void *value) { 131 if (isString(value)) { 132 CFStringRef cleanup_id = (CFStringRef) value; 133 // TODO: Since the enginePeers list is not authorative (the Account is) this could inadvertently clean up active peers or leave behind stale peers 134 if (enginePeers) CFArrayForEach(enginePeers, ^(const void *value) { 135 if (isString(value)) { 136 CFStringRef in_circle_id = (CFStringRef) value; 137 138 CFStringRef kvsKey = SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name, cleanup_id, in_circle_id); 139 SOSTransportMessageKVSAddToPendingChanges(transport, kvsKey, NULL); 140 CFReleaseSafe(kvsKey); 141 142 kvsKey = SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name, in_circle_id, cleanup_id); 143 SOSTransportMessageKVSAddToPendingChanges(transport, kvsKey, NULL); 144 CFReleaseSafe(kvsKey); 145 } 146 }); 147 148 } 149 }); 150 } 151 }); 152 153 return SOSTransportMessageFlushChanges((SOSTransportMessageRef)transport, error); 154} 155 156static CF_RETURNS_RETAINED 157CFDictionaryRef handleMessages(SOSTransportMessageRef transport, CFMutableDictionaryRef circle_peer_messages_table, CFErrorRef *error) { 158 SOSTransportMessageKVSRef tpt = (SOSTransportMessageKVSRef)transport; 159 CFMutableDictionaryRef handled = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault); 160 CFDictionaryRef peerToMessage = CFDictionaryGetValue(circle_peer_messages_table, tpt->circleName); 161 CFMutableArrayRef handled_peers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 162 CFDictionaryAddValue(handled, tpt->circleName, handled_peers); 163 164 if(peerToMessage){ 165 CFDictionaryForEach(peerToMessage, ^(const void *key, const void *value) { 166 CFStringRef peer_id = (CFStringRef) key; 167 CFDataRef peer_message = (CFDataRef) value; 168 CFErrorRef localError = NULL; 169 170 if (SOSTransportMessageHandlePeerMessage(transport, peer_id, peer_message, &localError)) { 171 CFArrayAppendValue(handled_peers, key); 172 } else { 173 secdebug("transport", "%@ KVSTransport handle message failed: %@", peer_id, localError); 174 } 175 CFReleaseNull(localError); 176 }); 177 } 178 CFReleaseNull(handled_peers); 179 180 return handled; 181} 182 183 184static bool sendToPeer(SOSTransportMessageRef transport, CFStringRef circleName, CFStringRef peerID, CFDataRef message, CFErrorRef *error) { 185 SOSTransportMessageKVSRef kvsTransport = (SOSTransportMessageKVSRef) transport; 186 bool result = true; 187 CFStringRef message_to_peer_key = SOSMessageKeyCreateFromTransportToPeer(kvsTransport, peerID); 188 CFDictionaryRef a_message_to_a_peer = CFDictionaryCreateForCFTypes(NULL, message_to_peer_key, message, NULL); 189 190 if (!SOSTransportMessageKVSUpdateKVS(kvsTransport, a_message_to_a_peer, error)) { 191 secerror("Sync with peers failed to send to %@ [%@], %@", peerID, a_message_to_a_peer, *error); 192 result = false; 193 } 194 CFReleaseNull(a_message_to_a_peer); 195 CFReleaseNull(message_to_peer_key); 196 197 return result; 198} 199 200static bool syncWithPeers(SOSTransportMessageRef transport, CFDictionaryRef circleToPeerIDs, CFErrorRef *error){ 201 // Each entry is keyed by circle name and contains a list of peerIDs 202 203 __block bool result = true; 204 205 CFDictionaryForEach(circleToPeerIDs, ^(const void *key, const void *value) { 206 if (isString(key) && isArray(value)) { 207 CFStringRef circleName = (CFStringRef) key; 208 CFArrayForEach(value, ^(const void *value) { 209 if (isString(value)) { 210 CFStringRef peerID = (CFStringRef) value; 211 result &= SOSTransportMessageSendMessageIfNeeded(transport, circleName, peerID, error); 212 } 213 }); 214 } 215 }); 216 217 return result; 218} 219 220static bool sendMessages(SOSTransportMessageRef transport, CFDictionaryRef circleToPeersToMessage, CFErrorRef *error) { 221 __block bool result = true; 222 223 CFDictionaryForEach(circleToPeersToMessage, ^(const void *key, const void *value) { 224 if (isString(key) && isDictionary(value)) { 225 CFStringRef circleName = (CFStringRef) key; 226 CFDictionaryForEach(value, ^(const void *key, const void *value) { 227 if (isString(key) && isData(value)) { 228 CFStringRef peerID = (CFStringRef) key; 229 CFDataRef message = (CFDataRef) value; 230 bool rx = sendToPeer(transport, circleName, peerID, message, error); 231 result &= rx; 232 } 233 }); 234 } 235 }); 236 237 return true; 238} 239 240static bool flushChanges(SOSTransportMessageRef transport, CFErrorRef *error) 241{ 242 return SOSTransportMessageKVSSendPendingChanges((SOSTransportMessageKVSRef) transport, error); 243} 244 245static bool cleanupAfterPeer(SOSTransportMessageRef transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error) 246{ 247 return SOSTransportMessageKVSCleanupAfterPeerMessages((SOSTransportMessageKVSRef) transport, circle_to_peer_ids, error); 248} 249 250