1 #include <SecureObjectSync/SOSTransport.h>
2 #include <SecureObjectSync/SOSTransportMessage.h>
3 #include <SecureObjectSync/SOSTransportMessageKVS.h>
4 #include <SecureObjectSync/SOSKVSKeys.h>
6 #include <utilities/SecCFWrappers.h>
7 #include <SOSInternal.h>
8 #include <AssertMacros.h>
9 #include <SOSCloudKeychainClient.h>
11 struct __OpaqueSOSTransportMessageKVS
{
12 struct __OpaqueSOSTransportMessage m
;
14 CFStringRef circleName
;
15 CFMutableDictionaryRef pending_changes
;
20 // V-table implementation forward declarations
22 static bool sendToPeer(SOSTransportMessageRef transport
, CFStringRef circleName
, CFStringRef peerID
, CFDataRef message
, CFErrorRef
*error
);
23 static bool syncWithPeers(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeerIDs
, CFErrorRef
*error
);
24 static bool sendMessages(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeersToMessage
, CFErrorRef
*error
);
25 static bool cleanupAfterPeer(SOSTransportMessageRef transport
, CFDictionaryRef circle_to_peer_ids
, CFErrorRef
*error
);
26 static void destroy(SOSTransportMessageRef transport
);
27 static CF_RETURNS_RETAINED
28 CFDictionaryRef
handleMessages(SOSTransportMessageRef transport
, CFMutableDictionaryRef circle_peer_messages_table
, CFErrorRef
*error
);
30 static bool flushChanges(SOSTransportMessageRef transport
, CFErrorRef
*error
);
33 CFStringRef
SOSTransportMessageKVSGetCircleName(SOSTransportMessageKVSRef transport
){
34 return transport
->circleName
;
37 SOSTransportMessageKVSRef
SOSTransportMessageKVSCreate(SOSAccountRef account
, CFStringRef circleName
, CFErrorRef
*error
){
38 SOSTransportMessageKVSRef tkvs
= (SOSTransportMessageKVSRef
) SOSTransportMessageCreateForSubclass(sizeof(struct __OpaqueSOSTransportMessageKVS
) - sizeof(CFRuntimeBase
), account
, circleName
, error
);
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
);
56 bool 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
);
61 CFArrayForEach(peer_ids
, ^(const void *value
) {
62 CFStringRef peerMessage
= SOSMessageKeyCreateFromPeerToTransport(transport
, value
);
63 CFArrayAppendValue(unlockedKeys
, peerMessage
);
64 CFReleaseNull(peerMessage
);
71 static void destroy(SOSTransportMessageRef transport
){
72 SOSTransportMessageKVSRef tkvs
= (SOSTransportMessageKVSRef
)transport
;
73 CFReleaseNull(tkvs
->circleName
);
74 CFReleaseNull(tkvs
->pending_changes
);
75 SOSUnregisterTransportMessage((SOSTransportMessageRef
)tkvs
);
79 static bool SOSTransportMessageKVSUpdateKVS(SOSTransportMessageKVSRef transport
, CFDictionaryRef changes
, CFErrorRef
*error
){
80 CloudKeychainReplyBlock log_error
= ^(CFDictionaryRef returnedValues __unused
, CFErrorRef error
) {
82 secerror("Error putting: %@", error
);
87 SOSCloudKeychainPutObjectsInCloud(changes
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), log_error
);
91 static bool SOSTransportMessageKVSSendPendingChanges(SOSTransportMessageKVSRef transport
, CFErrorRef
*error
) {
92 CFErrorRef changeError
= NULL
;
94 if (transport
->pending_changes
== NULL
|| CFDictionaryGetCount(transport
->pending_changes
) == 0) {
95 CFReleaseNull(transport
->pending_changes
);
99 bool success
= SOSTransportMessageKVSUpdateKVS(transport
, transport
->pending_changes
, &changeError
);
101 CFDictionaryRemoveAllValues(transport
->pending_changes
);
103 SOSCreateErrorWithFormat(kSOSErrorSendFailure
, changeError
, error
, NULL
,
104 CFSTR("Send changes block failed [%@]"), transport
->pending_changes
);
110 static void SOSTransportMessageKVSAddToPendingChanges(SOSTransportMessageKVSRef transport
, CFStringRef message_key
, CFDataRef message_data
){
111 if (transport
->pending_changes
== NULL
) {
112 transport
->pending_changes
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
114 if (message_data
== NULL
) {
115 CFDictionarySetValue(transport
->pending_changes
, message_key
, kCFNull
);
117 CFDictionarySetValue(transport
->pending_changes
, message_key
, message_data
);
121 static bool SOSTransportMessageKVSCleanupAfterPeerMessages(SOSTransportMessageKVSRef transport
, CFDictionaryRef circle_to_peer_ids
, CFErrorRef
*error
)
123 CFArrayRef enginePeers
= SOSEngineGetPeerIDs(SOSTransportMessageGetEngine((SOSTransportMessageRef
)transport
));
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
;
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
;
138 CFStringRef kvsKey
= SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name
, cleanup_id
, in_circle_id
);
139 SOSTransportMessageKVSAddToPendingChanges(transport
, kvsKey
, NULL
);
140 CFReleaseSafe(kvsKey
);
142 kvsKey
= SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name
, in_circle_id
, cleanup_id
);
143 SOSTransportMessageKVSAddToPendingChanges(transport
, kvsKey
, NULL
);
144 CFReleaseSafe(kvsKey
);
153 return SOSTransportMessageFlushChanges((SOSTransportMessageRef
)transport
, error
);
156 static CF_RETURNS_RETAINED
157 CFDictionaryRef
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
);
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
;
170 if (SOSTransportMessageHandlePeerMessage(transport
, peer_id
, peer_message
, &localError
)) {
171 CFArrayAppendValue(handled_peers
, key
);
173 secdebug("transport", "%@ KVSTransport handle message failed: %@", peer_id
, localError
);
175 CFReleaseNull(localError
);
178 CFReleaseNull(handled_peers
);
184 static bool sendToPeer(SOSTransportMessageRef transport
, CFStringRef circleName
, CFStringRef peerID
, CFDataRef message
, CFErrorRef
*error
) {
185 SOSTransportMessageKVSRef kvsTransport
= (SOSTransportMessageKVSRef
) transport
;
187 CFStringRef message_to_peer_key
= SOSMessageKeyCreateFromTransportToPeer(kvsTransport
, peerID
);
188 CFDictionaryRef a_message_to_a_peer
= CFDictionaryCreateForCFTypes(NULL
, message_to_peer_key
, message
, NULL
);
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
);
194 CFReleaseNull(a_message_to_a_peer
);
195 CFReleaseNull(message_to_peer_key
);
200 static bool syncWithPeers(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeerIDs
, CFErrorRef
*error
){
201 // Each entry is keyed by circle name and contains a list of peerIDs
203 __block
bool result
= true;
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
);
220 static bool sendMessages(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeersToMessage
, CFErrorRef
*error
) {
221 __block
bool result
= true;
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
);
240 static bool flushChanges(SOSTransportMessageRef transport
, CFErrorRef
*error
)
242 return SOSTransportMessageKVSSendPendingChanges((SOSTransportMessageKVSRef
) transport
, error
);
245 static bool cleanupAfterPeer(SOSTransportMessageRef transport
, CFDictionaryRef circle_to_peer_ids
, CFErrorRef
*error
)
247 return SOSTransportMessageKVSCleanupAfterPeerMessages((SOSTransportMessageKVSRef
) transport
, circle_to_peer_ids
, error
);