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
, CFSetRef peers
, CFErrorRef
*error
);
22 static bool sendMessages(SOSTransportMessageRef transport
, CFDictionaryRef peersToMessage
, 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 block_error
) {
88 secerror("Error putting: %@", block_error
);
92 SOSCloudKeychainPutObjectsInCloud(changes
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), log_error
);
96 static bool SOSTransportMessageKVSSendPendingChanges(SOSTransportMessageKVSRef transport
, CFErrorRef
*error
) {
97 CFErrorRef changeError
= NULL
;
99 if (transport
->pending_changes
== NULL
|| CFDictionaryGetCount(transport
->pending_changes
) == 0) {
100 CFReleaseNull(transport
->pending_changes
);
103 SOSAccountRef account
= SOSTransportMessageGetAccount((SOSTransportMessageRef
)transport
);
104 CFTypeRef dsid
= SOSAccountGetValue(account
, kSOSDSIDKey
, error
);
109 CFDictionaryAddValue(transport
->pending_changes
, kSOSKVSRequiredKey
, dsid
);
111 bool success
= SOSTransportMessageKVSUpdateKVS(transport
, transport
->pending_changes
, &changeError
);
113 CFDictionaryRemoveAllValues(transport
->pending_changes
);
115 SOSCreateErrorWithFormat(kSOSErrorSendFailure
, changeError
, error
, NULL
,
116 CFSTR("Send changes block failed [%@]"), transport
->pending_changes
);
122 static void SOSTransportMessageKVSAddToPendingChanges(SOSTransportMessageKVSRef transport
, CFStringRef message_key
, CFDataRef message_data
){
123 if (transport
->pending_changes
== NULL
) {
124 transport
->pending_changes
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
126 if (message_data
== NULL
) {
127 CFDictionarySetValue(transport
->pending_changes
, message_key
, kCFNull
);
129 CFDictionarySetValue(transport
->pending_changes
, message_key
, message_data
);
133 static bool SOSTransportMessageKVSCleanupAfterPeerMessages(SOSTransportMessageKVSRef transport
, CFDictionaryRef circle_to_peer_ids
, CFErrorRef
*error
)
135 CFArrayRef enginePeers
= SOSEngineGetPeerIDs(SOSTransportMessageGetEngine((SOSTransportMessageRef
)transport
));
136 __block SOSAccountRef account
= SOSTransportMessageGetAccount((SOSTransportMessageRef
)transport
);
138 CFDictionaryForEach(circle_to_peer_ids
, ^(const void *key
, const void *value
) {
139 if (isString(key
) && isArray(value
)) {
140 CFStringRef circle_name
= (CFStringRef
) key
;
141 CFArrayRef peers_to_cleanup_after
= (CFArrayRef
) value
;
143 CFArrayForEach(peers_to_cleanup_after
, ^(const void *value
) {
144 if (isString(value
)) {
145 CFStringRef cleanup_id
= (CFStringRef
) value
;
146 // TODO: Since the enginePeers list is not authorative (the Account is) this could inadvertently clean up active peers or leave behind stale peers
147 if (enginePeers
) CFArrayForEach(enginePeers
, ^(const void *value
) {
148 if (isString(value
)) {
149 CFStringRef in_circle_id
= (CFStringRef
) value
;
151 CFStringRef kvsKey
= SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name
, cleanup_id
, in_circle_id
);
152 SOSTransportMessageKVSAddToPendingChanges(transport
, kvsKey
, NULL
);
153 CFReleaseSafe(kvsKey
);
155 CFStringRef lastCirclePushedKey
= SOSLastCirclePushedKeyCreateWithCircleNameAndPeerID(circle_name
, cleanup_id
);
156 SOSTransportMessageKVSAddToPendingChanges(transport
, lastCirclePushedKey
, NULL
);
157 CFReleaseSafe(lastCirclePushedKey
);
159 CFStringRef lastKeyParameterPushedKey
= SOSLastKeyParametersPushedKeyCreateWithPeerID(cleanup_id
);
160 SOSTransportMessageKVSAddToPendingChanges(transport
, lastKeyParameterPushedKey
, NULL
);
161 CFReleaseSafe(lastKeyParameterPushedKey
);
163 CFStringRef lastCirclePushedWithAccountGestaltKey
= SOSLastCirclePushedKeyCreateWithAccountGestalt(account
);
164 SOSTransportMessageKVSAddToPendingChanges(transport
, lastCirclePushedKey
, NULL
);
165 CFReleaseSafe(lastCirclePushedWithAccountGestaltKey
);
167 CFStringRef lastKeyParameterWithAccountGestaltKey
= SOSLastKeyParametersPushedKeyCreateWithAccountGestalt(account
);
168 SOSTransportMessageKVSAddToPendingChanges(transport
, lastCirclePushedKey
, NULL
);
169 CFReleaseSafe(lastKeyParameterWithAccountGestaltKey
);
171 kvsKey
= SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name
, in_circle_id
, cleanup_id
);
172 SOSTransportMessageKVSAddToPendingChanges(transport
, kvsKey
, NULL
);
173 CFReleaseSafe(kvsKey
);
182 return SOSTransportMessageFlushChanges((SOSTransportMessageRef
)transport
, error
);
185 static CF_RETURNS_RETAINED
186 CFDictionaryRef
handleMessages(SOSTransportMessageRef transport
, CFMutableDictionaryRef circle_peer_messages_table
, CFErrorRef
*error
) {
188 CFMutableDictionaryRef handled
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
189 CFDictionaryRef peerToMessage
= CFDictionaryGetValue(circle_peer_messages_table
, transport
->circleName
);
190 CFMutableArrayRef handled_peers
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
193 CFDictionaryForEach(peerToMessage
, ^(const void *key
, const void *value
) {
194 CFStringRef peer_id
= asString(key
, NULL
);
195 CFDataRef peer_message
= asData(value
, NULL
);
196 CFErrorRef localError
= NULL
;
198 if (peer_id
&& peer_message
&& SOSTransportMessageHandlePeerMessage(transport
, peer_id
, peer_message
, &localError
)) {
199 CFArrayAppendValue(handled_peers
, key
);
201 secnotice("transport", "%@ KVSTransport handle message failed: %@", peer_id
, localError
);
203 CFReleaseNull(localError
);
206 CFDictionaryAddValue(handled
, transport
->circleName
, handled_peers
);
207 CFReleaseNull(handled_peers
);
213 static bool sendToPeer(SOSTransportMessageRef transport
, CFStringRef circleName
, CFStringRef peerID
, CFDataRef message
, CFErrorRef
*error
) {
214 SOSTransportMessageKVSRef kvsTransport
= (SOSTransportMessageKVSRef
) transport
;
216 SOSAccountRef account
= SOSTransportMessageGetAccount((SOSTransportMessageRef
)transport
);
217 CFTypeRef dsid
= SOSAccountGetValue(account
, kSOSDSIDKey
, error
);
222 CFStringRef message_to_peer_key
= SOSMessageKeyCreateFromTransportToPeer((SOSTransportMessageRef
)kvsTransport
, peerID
);
224 CFTypeRef messageToSend
= message
!= NULL
? (CFTypeRef
) message
: (CFTypeRef
) kCFNull
;
225 CFDictionaryRef a_message_to_a_peer
= CFDictionaryCreateForCFTypes(NULL
,
226 message_to_peer_key
, messageToSend
,
227 kSOSKVSRequiredKey
, dsid
,
230 if (!SOSTransportMessageKVSUpdateKVS(kvsTransport
, a_message_to_a_peer
, error
)) {
231 secerror("Sync with peers failed to send to %@ [%@], %@", peerID
, a_message_to_a_peer
, *error
);
234 CFReleaseNull(a_message_to_a_peer
);
235 CFReleaseNull(message_to_peer_key
);
240 static bool syncWithPeers(SOSTransportMessageRef transport
, CFSetRef peers
, CFErrorRef
*error
) {
241 // Each entry is keyed by circle name and contains a list of peerIDs
242 __block
bool result
= true;
244 CFSetForEach(peers
, ^(const void *value
) {
245 CFStringRef peerID
= asString(value
, NULL
);
246 result
&= SOSTransportMessageSendMessageIfNeeded(transport
, transport
->circleName
, peerID
, error
);
252 static bool sendMessages(SOSTransportMessageRef transport
, CFDictionaryRef peersToMessage
, CFErrorRef
*error
) {
253 __block
bool result
= true;
255 CFStringRef circleName
= transport
->circleName
;
256 CFDictionaryForEach(peersToMessage
, ^(const void *key
, const void *value
) {
257 CFStringRef peerID
= asString(key
, NULL
);
258 CFDataRef message
= asData(value
,NULL
);
259 if (peerID
&& message
) {
260 bool rx
= sendToPeer(transport
, circleName
, peerID
, message
, error
);
268 static bool flushChanges(SOSTransportMessageRef transport
, CFErrorRef
*error
)
270 return SOSTransportMessageKVSSendPendingChanges((SOSTransportMessageKVSRef
) transport
, error
);
273 static bool cleanupAfterPeer(SOSTransportMessageRef transport
, CFDictionaryRef circle_to_peer_ids
, CFErrorRef
*error
)
275 return SOSTransportMessageKVSCleanupAfterPeerMessages((SOSTransportMessageKVSRef
) transport
, circle_to_peer_ids
, error
);