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
);
55 CFArrayRef peerInfos
= SOSAccountCopyPeersToListenTo(SOSTransportMessageGetAccount((SOSTransportMessageRef
) transport
), localError
);
58 CFArrayForEach(peerInfos
, ^(const void *value
) {
59 SOSPeerInfoRef peer
= (SOSPeerInfoRef
)value
;
60 CFStringRef peerID
= SOSPeerInfoGetPeerID(peer
);
61 CFStringRef peerMessage
= SOSMessageKeyCreateFromPeerToTransport(transport
, peerID
);
62 CFArrayAppendValue(unlockedKeys
, peerMessage
);
63 CFReleaseNull(peerMessage
);
65 CFReleaseNull(peerInfos
);
71 static void destroy(SOSTransportMessageRef transport
){
72 SOSTransportMessageKVSRef tkvs
= (SOSTransportMessageKVSRef
)transport
;
73 CFReleaseNull(tkvs
->pending_changes
);
74 SOSUnregisterTransportMessage((SOSTransportMessageRef
)tkvs
);
78 static inline CFIndex
getTransportType(SOSTransportMessageRef transport
, CFErrorRef
*error
){
82 static bool SOSTransportMessageKVSUpdateKVS(SOSTransportMessageKVSRef transport
, CFDictionaryRef changes
, CFErrorRef
*error
){
84 CloudKeychainReplyBlock log_error
= ^(CFDictionaryRef returnedValues __unused
, CFErrorRef error
) {
86 secerror("Error putting: %@", error
);
91 SOSCloudKeychainPutObjectsInCloud(changes
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), log_error
);
95 static bool SOSTransportMessageKVSSendPendingChanges(SOSTransportMessageKVSRef transport
, CFErrorRef
*error
) {
96 CFErrorRef changeError
= NULL
;
98 if (transport
->pending_changes
== NULL
|| CFDictionaryGetCount(transport
->pending_changes
) == 0) {
99 CFReleaseNull(transport
->pending_changes
);
102 SOSAccountRef account
= SOSTransportMessageGetAccount((SOSTransportMessageRef
)transport
);
103 CFTypeRef dsid
= SOSAccountGetValue(account
, kSOSDSIDKey
, error
);
108 CFDictionaryAddValue(transport
->pending_changes
, kSOSKVSRequiredKey
, dsid
);
110 bool success
= SOSTransportMessageKVSUpdateKVS(transport
, transport
->pending_changes
, &changeError
);
112 CFDictionaryRemoveAllValues(transport
->pending_changes
);
114 SOSCreateErrorWithFormat(kSOSErrorSendFailure
, changeError
, error
, NULL
,
115 CFSTR("Send changes block failed [%@]"), transport
->pending_changes
);
121 static void SOSTransportMessageKVSAddToPendingChanges(SOSTransportMessageKVSRef transport
, CFStringRef message_key
, CFDataRef message_data
){
122 if (transport
->pending_changes
== NULL
) {
123 transport
->pending_changes
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
125 if (message_data
== NULL
) {
126 CFDictionarySetValue(transport
->pending_changes
, message_key
, kCFNull
);
128 CFDictionarySetValue(transport
->pending_changes
, message_key
, message_data
);
132 static bool SOSTransportMessageKVSCleanupAfterPeerMessages(SOSTransportMessageKVSRef transport
, CFDictionaryRef circle_to_peer_ids
, CFErrorRef
*error
)
134 CFArrayRef enginePeers
= SOSEngineGetPeerIDs(SOSTransportMessageGetEngine((SOSTransportMessageRef
)transport
));
135 __block SOSAccountRef account
= SOSTransportMessageGetAccount((SOSTransportMessageRef
)transport
);
137 CFDictionaryForEach(circle_to_peer_ids
, ^(const void *key
, const void *value
) {
138 if (isString(key
) && isArray(value
)) {
139 CFStringRef circle_name
= (CFStringRef
) key
;
140 CFArrayRef peers_to_cleanup_after
= (CFArrayRef
) value
;
142 CFArrayForEach(peers_to_cleanup_after
, ^(const void *value
) {
143 if (isString(value
)) {
144 CFStringRef cleanup_id
= (CFStringRef
) value
;
145 // TODO: Since the enginePeers list is not authorative (the Account is) this could inadvertently clean up active peers or leave behind stale peers
146 if (enginePeers
) CFArrayForEach(enginePeers
, ^(const void *value
) {
147 if (isString(value
)) {
148 CFStringRef in_circle_id
= (CFStringRef
) value
;
150 CFStringRef kvsKey
= SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name
, cleanup_id
, in_circle_id
);
151 SOSTransportMessageKVSAddToPendingChanges(transport
, kvsKey
, NULL
);
152 CFReleaseSafe(kvsKey
);
154 CFStringRef lastCirclePushedKey
= SOSLastCirclePushedKeyCreateWithCircleNameAndPeerID(circle_name
, cleanup_id
);
155 SOSTransportMessageKVSAddToPendingChanges(transport
, lastCirclePushedKey
, NULL
);
156 CFReleaseSafe(lastCirclePushedKey
);
158 CFStringRef lastKeyParameterPushedKey
= SOSLastKeyParametersPushedKeyCreateWithPeerID(cleanup_id
);
159 SOSTransportMessageKVSAddToPendingChanges(transport
, lastKeyParameterPushedKey
, NULL
);
160 CFReleaseSafe(lastKeyParameterPushedKey
);
162 CFStringRef lastCirclePushedWithAccountGestaltKey
= SOSLastCirclePushedKeyCreateWithAccountGestalt(account
);
163 SOSTransportMessageKVSAddToPendingChanges(transport
, lastCirclePushedKey
, NULL
);
164 CFReleaseSafe(lastCirclePushedWithAccountGestaltKey
);
166 CFStringRef lastKeyParameterWithAccountGestaltKey
= SOSLastKeyParametersPushedKeyCreateWithAccountGestalt(account
);
167 SOSTransportMessageKVSAddToPendingChanges(transport
, lastCirclePushedKey
, NULL
);
168 CFReleaseSafe(lastKeyParameterWithAccountGestaltKey
);
170 kvsKey
= SOSMessageKeyCreateWithCircleNameAndPeerNames(circle_name
, in_circle_id
, cleanup_id
);
171 SOSTransportMessageKVSAddToPendingChanges(transport
, kvsKey
, NULL
);
172 CFReleaseSafe(kvsKey
);
181 return SOSTransportMessageFlushChanges((SOSTransportMessageRef
)transport
, error
);
184 static CF_RETURNS_RETAINED
185 CFDictionaryRef
handleMessages(SOSTransportMessageRef transport
, CFMutableDictionaryRef circle_peer_messages_table
, CFErrorRef
*error
) {
187 CFMutableDictionaryRef handled
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
188 CFDictionaryRef peerToMessage
= CFDictionaryGetValue(circle_peer_messages_table
, transport
->circleName
);
189 CFMutableArrayRef handled_peers
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
192 CFDictionaryForEach(peerToMessage
, ^(const void *key
, const void *value
) {
193 CFStringRef peer_id
= (CFStringRef
) key
;
194 CFDataRef peer_message
= (CFDataRef
) value
;
195 CFErrorRef localError
= NULL
;
197 if (SOSTransportMessageHandlePeerMessage(transport
, peer_id
, peer_message
, &localError
)) {
198 CFArrayAppendValue(handled_peers
, key
);
200 secnotice("transport", "%@ KVSTransport handle message failed: %@", peer_id
, localError
);
202 CFReleaseNull(localError
);
205 CFDictionaryAddValue(handled
, transport
->circleName
, handled_peers
);
206 CFReleaseNull(handled_peers
);
212 static bool sendToPeer(SOSTransportMessageRef transport
, CFStringRef circleName
, CFStringRef peerID
, CFDataRef message
, CFErrorRef
*error
) {
213 SOSTransportMessageKVSRef kvsTransport
= (SOSTransportMessageKVSRef
) transport
;
215 SOSAccountRef account
= SOSTransportMessageGetAccount((SOSTransportMessageRef
)transport
);
216 CFTypeRef dsid
= SOSAccountGetValue(account
, kSOSDSIDKey
, error
);
221 CFStringRef message_to_peer_key
= SOSMessageKeyCreateFromTransportToPeer(kvsTransport
, peerID
);
222 CFDictionaryRef a_message_to_a_peer
= CFDictionaryCreateForCFTypes(NULL
, message_to_peer_key
, message
, kSOSKVSRequiredKey
, dsid
, NULL
);
224 if (!SOSTransportMessageKVSUpdateKVS(kvsTransport
, a_message_to_a_peer
, error
)) {
225 secerror("Sync with peers failed to send to %@ [%@], %@", peerID
, a_message_to_a_peer
, *error
);
228 CFReleaseNull(a_message_to_a_peer
);
229 CFReleaseNull(message_to_peer_key
);
234 static bool syncWithPeers(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeerIDs
, CFErrorRef
*error
) {
235 // Each entry is keyed by circle name and contains a list of peerIDs
236 __block
bool result
= true;
238 CFDictionaryForEach(circleToPeerIDs
, ^(const void *key
, const void *value
) {
239 if (isString(key
) && isArray(value
)) {
240 CFStringRef circleName
= (CFStringRef
) key
;
241 CFArrayForEach(value
, ^(const void *value
) {
242 if (isString(value
)) {
243 CFStringRef peerID
= (CFStringRef
) value
;
244 result
&= SOSTransportMessageSendMessageIfNeeded(transport
, circleName
, peerID
, error
);
253 static bool sendMessages(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeersToMessage
, CFErrorRef
*error
) {
254 __block
bool result
= true;
256 CFDictionaryForEach(circleToPeersToMessage
, ^(const void *key
, const void *value
) {
257 if (isString(key
) && isDictionary(value
)) {
258 CFStringRef circleName
= (CFStringRef
) key
;
259 CFDictionaryForEach(value
, ^(const void *key
, const void *value
) {
260 if (isString(key
) && isData(value
)) {
261 CFStringRef peerID
= (CFStringRef
) key
;
262 CFDataRef message
= (CFDataRef
) value
;
263 bool rx
= sendToPeer(transport
, circleName
, peerID
, message
, error
);
273 static bool flushChanges(SOSTransportMessageRef transport
, CFErrorRef
*error
)
275 return SOSTransportMessageKVSSendPendingChanges((SOSTransportMessageKVSRef
) transport
, error
);
278 static bool cleanupAfterPeer(SOSTransportMessageRef transport
, CFDictionaryRef circle_to_peer_ids
, CFErrorRef
*error
)
280 return SOSTransportMessageKVSCleanupAfterPeerMessages((SOSTransportMessageKVSRef
) transport
, circle_to_peer_ids
, error
);