1 #include <Security/SecureObjectSync/SOSTransport.h>
2 #include <Security/SecureObjectSync/SOSTransportMessage.h>
3 #include <Security/SecureObjectSync/SOSTransportMessageKVS.h>
4 #include <Security/SecureObjectSync/SOSKVSKeys.h>
5 #include <Security/SecureObjectSync/SOSAccountPriv.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
;
13 CFMutableDictionaryRef pending_changes
;
18 // V-table implementation forward declarations
20 static bool sendToPeer(SOSTransportMessageRef transport
, CFStringRef circleName
, CFStringRef peerID
, CFDataRef message
, CFErrorRef
*error
);
21 static bool syncWithPeers(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeerIDs
, CFErrorRef
*error
);
22 static bool sendMessages(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeersToMessage
, CFErrorRef
*error
);
23 static bool cleanupAfterPeer(SOSTransportMessageRef transport
, CFDictionaryRef circle_to_peer_ids
, CFErrorRef
*error
);
24 static void destroy(SOSTransportMessageRef transport
);
25 static inline CFIndex
getTransportType(SOSTransportMessageRef transport
, CFErrorRef
*error
);
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
);
32 SOSTransportMessageKVSRef
SOSTransportMessageKVSCreate(SOSAccountRef account
, CFStringRef circleName
, CFErrorRef
*error
){
33 SOSTransportMessageKVSRef tkvs
= (SOSTransportMessageKVSRef
) SOSTransportMessageCreateForSubclass(sizeof(struct __OpaqueSOSTransportMessageKVS
) - sizeof(CFRuntimeBase
), account
, circleName
, error
);
37 tkvs
->m
.sendMessages
= sendMessages
;
38 tkvs
->m
.syncWithPeers
= syncWithPeers
;
39 tkvs
->m
.flushChanges
= flushChanges
;
40 tkvs
->m
.cleanupAfterPeerMessages
= cleanupAfterPeer
;
41 tkvs
->m
.destroy
= destroy
;
42 tkvs
->m
.handleMessages
= handleMessages
;
43 tkvs
->m
.getTransportType
= getTransportType
;
44 // Initialize ourselves
45 tkvs
->pending_changes
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
46 SOSRegisterTransportMessage((SOSTransportMessageRef
)tkvs
);
52 bool SOSTransportMessageKVSAppendKeyInterest(SOSTransportMessageKVSRef transport
, CFMutableArrayRef alwaysKeys
, CFMutableArrayRef afterFirstUnlockKeys
, CFMutableArrayRef unlockedKeys
, CFErrorRef
*localError
){
53 SOSEngineRef engine
= SOSTransportMessageGetEngine((SOSTransportMessageRef
)transport
);
54 require_quiet(engine
, fail
);
56 CFArrayRef peerInfos
= SOSAccountCopyPeersToListenTo(SOSTransportMessageGetAccount((SOSTransportMessageRef
) transport
), localError
);
59 CFArrayForEach(peerInfos
, ^(const void *value
) {
60 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
61 CFStringRef peerID
= SOSPeerInfoGetPeerID(peer
);
62 CFStringRef peerMessage
= SOSMessageKeyCreateFromPeerToTransport((SOSTransportMessageRef
)transport
, peerID
);
63 if(peerMessage
!= NULL
)
64 CFArrayAppendValue(unlockedKeys
, peerMessage
);
65 CFReleaseNull(peerMessage
);
67 CFReleaseNull(peerInfos
);
73 static void destroy(SOSTransportMessageRef transport
){
74 SOSTransportMessageKVSRef tkvs
= (SOSTransportMessageKVSRef
)transport
;
75 CFReleaseNull(tkvs
->pending_changes
);
76 SOSUnregisterTransportMessage((SOSTransportMessageRef
)tkvs
);
80 static inline CFIndex
getTransportType(SOSTransportMessageRef transport
, CFErrorRef
*error
){
84 static bool SOSTransportMessageKVSUpdateKVS(SOSTransportMessageKVSRef transport
, CFDictionaryRef changes
, CFErrorRef
*error
){
86 CloudKeychainReplyBlock log_error
= ^(CFDictionaryRef returnedValues __unused
, CFErrorRef error
) {
88 secerror("Error putting: %@", error
);
93 SOSCloudKeychainPutObjectsInCloud(changes
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), log_error
);
97 static bool SOSTransportMessageKVSSendPendingChanges(SOSTransportMessageKVSRef transport
, CFErrorRef
*error
) {
98 CFErrorRef changeError
= NULL
;
100 if (transport
->pending_changes
== NULL
|| CFDictionaryGetCount(transport
->pending_changes
) == 0) {
101 CFReleaseNull(transport
->pending_changes
);
104 SOSAccountRef account
= SOSTransportMessageGetAccount((SOSTransportMessageRef
)transport
);
105 CFTypeRef dsid
= SOSAccountGetValue(account
, kSOSDSIDKey
, error
);
110 CFDictionaryAddValue(transport
->pending_changes
, kSOSKVSRequiredKey
, dsid
);
112 bool success
= SOSTransportMessageKVSUpdateKVS(transport
, transport
->pending_changes
, &changeError
);
114 CFDictionaryRemoveAllValues(transport
->pending_changes
);
116 SOSCreateErrorWithFormat(kSOSErrorSendFailure
, changeError
, error
, NULL
,
117 CFSTR("Send changes block failed [%@]"), transport
->pending_changes
);
123 static void SOSTransportMessageKVSAddToPendingChanges(SOSTransportMessageKVSRef transport
, CFStringRef message_key
, CFDataRef message_data
){
124 if (transport
->pending_changes
== NULL
) {
125 transport
->pending_changes
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
127 if (message_data
== NULL
) {
128 CFDictionarySetValue(transport
->pending_changes
, message_key
, kCFNull
);
130 CFDictionarySetValue(transport
->pending_changes
, message_key
, message_data
);
134 static bool SOSTransportMessageKVSCleanupAfterPeerMessages(SOSTransportMessageKVSRef transport
, CFDictionaryRef circle_to_peer_ids
, CFErrorRef
*error
)
136 CFArrayRef enginePeers
= SOSEngineGetPeerIDs(SOSTransportMessageGetEngine((SOSTransportMessageRef
)transport
));
137 __block SOSAccountRef account
= SOSTransportMessageGetAccount((SOSTransportMessageRef
)transport
);
139 CFDictionaryForEach(circle_to_peer_ids
, ^(const void *key
, const void *value
) {
140 if (isString(key
) && isArray(value
)) {
141 CFStringRef circle_name
= (CFStringRef
) key
;
142 CFArrayRef peers_to_cleanup_after
= (CFArrayRef
) value
;
144 CFArrayForEach(peers_to_cleanup_after
, ^(const void *value
) {
145 if (isString(value
)) {
146 CFStringRef cleanup_id
= (CFStringRef
) value
;
147 // TODO: Since the enginePeers list is not authorative (the Account is) this could inadvertently clean up active peers or leave behind stale peers
148 if (enginePeers
) CFArrayForEach(enginePeers
, ^(const void *value
) {
149 if (isString(value
)) {
150 CFStringRef in_circle_id
= (CFStringRef
) value
;
152 CFStringRef kvsKey
= SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name
, cleanup_id
, in_circle_id
);
153 SOSTransportMessageKVSAddToPendingChanges(transport
, kvsKey
, NULL
);
154 CFReleaseSafe(kvsKey
);
156 CFStringRef lastCirclePushedKey
= SOSLastCirclePushedKeyCreateWithCircleNameAndPeerID(circle_name
, cleanup_id
);
157 SOSTransportMessageKVSAddToPendingChanges(transport
, lastCirclePushedKey
, NULL
);
158 CFReleaseSafe(lastCirclePushedKey
);
160 CFStringRef lastKeyParameterPushedKey
= SOSLastKeyParametersPushedKeyCreateWithPeerID(cleanup_id
);
161 SOSTransportMessageKVSAddToPendingChanges(transport
, lastKeyParameterPushedKey
, NULL
);
162 CFReleaseSafe(lastKeyParameterPushedKey
);
164 CFStringRef lastCirclePushedWithAccountGestaltKey
= SOSLastCirclePushedKeyCreateWithAccountGestalt(account
);
165 SOSTransportMessageKVSAddToPendingChanges(transport
, lastCirclePushedKey
, NULL
);
166 CFReleaseSafe(lastCirclePushedWithAccountGestaltKey
);
168 CFStringRef lastKeyParameterWithAccountGestaltKey
= SOSLastKeyParametersPushedKeyCreateWithAccountGestalt(account
);
169 SOSTransportMessageKVSAddToPendingChanges(transport
, lastCirclePushedKey
, NULL
);
170 CFReleaseSafe(lastKeyParameterWithAccountGestaltKey
);
172 kvsKey
= SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name
, in_circle_id
, cleanup_id
);
173 SOSTransportMessageKVSAddToPendingChanges(transport
, kvsKey
, NULL
);
174 CFReleaseSafe(kvsKey
);
183 return SOSTransportMessageFlushChanges((SOSTransportMessageRef
)transport
, error
);
186 static CF_RETURNS_RETAINED
187 CFDictionaryRef
handleMessages(SOSTransportMessageRef transport
, CFMutableDictionaryRef circle_peer_messages_table
, CFErrorRef
*error
) {
189 CFMutableDictionaryRef handled
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
190 CFDictionaryRef peerToMessage
= CFDictionaryGetValue(circle_peer_messages_table
, transport
->circleName
);
191 CFMutableArrayRef handled_peers
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
194 CFDictionaryForEach(peerToMessage
, ^(const void *key
, const void *value
) {
195 CFStringRef peer_id
= asString(key
, NULL
);
196 CFDataRef peer_message
= asData(value
, NULL
);
197 CFErrorRef localError
= NULL
;
199 if (peer_id
&& peer_message
&& SOSTransportMessageHandlePeerMessage(transport
, peer_id
, peer_message
, &localError
)) {
200 CFArrayAppendValue(handled_peers
, key
);
202 secnotice("transport", "%@ KVSTransport handle message failed: %@", peer_id
, localError
);
204 CFReleaseNull(localError
);
207 CFDictionaryAddValue(handled
, transport
->circleName
, handled_peers
);
208 CFReleaseNull(handled_peers
);
214 static bool sendToPeer(SOSTransportMessageRef transport
, CFStringRef circleName
, CFStringRef peerID
, CFDataRef message
, CFErrorRef
*error
) {
215 SOSTransportMessageKVSRef kvsTransport
= (SOSTransportMessageKVSRef
) transport
;
217 SOSAccountRef account
= SOSTransportMessageGetAccount((SOSTransportMessageRef
)transport
);
218 CFTypeRef dsid
= SOSAccountGetValue(account
, kSOSDSIDKey
, error
);
223 CFStringRef message_to_peer_key
= SOSMessageKeyCreateFromTransportToPeer((SOSTransportMessageRef
)kvsTransport
, peerID
);
224 CFDictionaryRef a_message_to_a_peer
= CFDictionaryCreateForCFTypes(NULL
, message_to_peer_key
, message
, kSOSKVSRequiredKey
, dsid
, NULL
);
226 if (!SOSTransportMessageKVSUpdateKVS(kvsTransport
, a_message_to_a_peer
, error
)) {
227 secerror("Sync with peers failed to send to %@ [%@], %@", peerID
, a_message_to_a_peer
, *error
);
230 CFReleaseNull(a_message_to_a_peer
);
231 CFReleaseNull(message_to_peer_key
);
236 static bool syncWithPeers(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeerIDs
, CFErrorRef
*error
) {
237 // Each entry is keyed by circle name and contains a list of peerIDs
238 __block
bool result
= true;
240 CFDictionaryForEach(circleToPeerIDs
, ^(const void *key
, const void *value
) {
241 if (isString(key
) && isArray(value
)) {
242 CFStringRef circleName
= (CFStringRef
) key
;
243 CFArrayForEach(value
, ^(const void *value
) {
244 if (isString(value
)) {
245 CFStringRef peerID
= (CFStringRef
) value
;
246 result
&= SOSTransportMessageSendMessageIfNeeded(transport
, circleName
, peerID
, error
);
255 static bool sendMessages(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeersToMessage
, CFErrorRef
*error
) {
256 __block
bool result
= true;
258 CFDictionaryForEach(circleToPeersToMessage
, ^(const void *key
, const void *value
) {
259 if (isString(key
) && isDictionary(value
)) {
260 CFStringRef circleName
= (CFStringRef
) key
;
261 CFDictionaryForEach(value
, ^(const void *key
, const void *value
) {
262 if (isString(key
) && isData(value
)) {
263 CFStringRef peerID
= (CFStringRef
) key
;
264 CFDataRef message
= (CFDataRef
) value
;
265 bool rx
= sendToPeer(transport
, circleName
, peerID
, message
, error
);
275 static bool flushChanges(SOSTransportMessageRef transport
, CFErrorRef
*error
)
277 return SOSTransportMessageKVSSendPendingChanges((SOSTransportMessageKVSRef
) transport
, error
);
280 static bool cleanupAfterPeer(SOSTransportMessageRef transport
, CFDictionaryRef circle_to_peer_ids
, CFErrorRef
*error
)
282 return SOSTransportMessageKVSCleanupAfterPeerMessages((SOSTransportMessageKVSRef
) transport
, circle_to_peer_ids
, error
);