2 // SOSTransportMessageIDS.c
6 #include <Security/SecBasePriv.h>
7 #include <Security/SecureObjectSync/SOSTransport.h>
8 #include <Security/SecureObjectSync/SOSTransportMessage.h>
9 #include <Security/SecureObjectSync/SOSKVSKeys.h>
10 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
12 #include <SOSCloudCircleServer.h>
13 #include <Security/SecureObjectSync/SOSAccountPriv.h>
14 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
16 #include <utilities/SecCFWrappers.h>
17 #include <SOSInternal.h>
18 #include <AssertMacros.h>
20 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
21 #include <SOSCircle/CKBridge/SOSCloudKeychainConstants.h>
24 #define IDS "IDS transport"
26 struct __OpaqueSOSTransportMessageIDS
{
27 struct __OpaqueSOSTransportMessage m
;
31 const CFStringRef kSecIDSErrorDomain
= CFSTR("com.apple.security.ids.error");
35 // V-table implementation forward declarations
37 static bool sendToPeer(SOSTransportMessageRef transport
, CFStringRef circleName
, CFStringRef deviceID
, CFStringRef peerID
, idsOperation whichOTRType
, CFDataRef message
, CFErrorRef
*error
);
38 static bool syncWithPeers(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeerIDs
, CFErrorRef
*error
);
39 static bool sendMessages(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeersToMessage
, CFErrorRef
*error
);
40 static void destroy(SOSTransportMessageRef transport
);
41 static bool cleanupAfterPeer(SOSTransportMessageRef transport
, CFDictionaryRef circle_to_peer_ids
, CFErrorRef
*error
);
42 static bool flushChanges(SOSTransportMessageRef transport
, CFErrorRef
*error
);
43 static CF_RETURNS_RETAINED CFDictionaryRef
handleMessages(SOSTransportMessageRef transport
, CFMutableDictionaryRef circle_peer_messages_table
, CFErrorRef
*error
);
45 static inline CFIndex
getTransportType(SOSTransportMessageRef transport
, CFErrorRef
*error
){
49 SOSTransportMessageIDSRef
SOSTransportMessageIDSCreate(SOSAccountRef account
, CFStringRef circleName
, CFErrorRef
*error
)
51 SOSTransportMessageIDSRef ids
= (SOSTransportMessageIDSRef
) SOSTransportMessageCreateForSubclass(sizeof(struct __OpaqueSOSTransportMessageIDS
) - sizeof(CFRuntimeBase
), account
, circleName
, error
);
55 ids
->m
.sendMessages
= sendMessages
;
56 ids
->m
.syncWithPeers
= syncWithPeers
;
57 ids
->m
.flushChanges
= flushChanges
;
58 ids
->m
.cleanupAfterPeerMessages
= cleanupAfterPeer
;
59 ids
->m
.destroy
= destroy
;
60 ids
->m
.handleMessages
= handleMessages
;
61 ids
->m
.getTransportType
= getTransportType
;
63 // Initialize ourselves
64 if ((whichTransportType
== kSOSTransportIDS
|| whichTransportType
== kSOSTransportFuture
|| whichTransportType
== kSOSTransportPresent
) && account
->ids_message_transport
) {
65 CFStringRef deviceID
= SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
66 if(deviceID
== NULL
|| CFStringGetLength(deviceID
) == 0){
68 __block
bool success
= false;
70 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
){
71 success
= (sync_error
== NULL
);
73 CFRetainAssign(*error
, sync_error
);
78 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", *error
);
81 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
84 CFReleaseNull(deviceID
);
87 if (whichTransportType
== kSOSTransportIDS
) {
88 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(account
);
90 __block
bool success
= false;
91 CFStringRef deviceIDRefreshed
= SOSPeerInfoCopyDeviceID(myPeer
);
92 if(!deviceIDRefreshed
|| 0 == CFStringGetLength(deviceIDRefreshed
)){
93 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
){
94 success
= (sync_error
== NULL
);
96 CFRetainAssign(*error
, sync_error
);
101 secerror("Could not ask IDSKeychainSyncingProxy for Device ID");
104 secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
106 CFReleaseNull(deviceIDRefreshed
);
111 SOSRegisterTransportMessage((SOSTransportMessageRef
)ids
);
116 static void destroy(SOSTransportMessageRef transport
){
117 SOSUnregisterTransportMessage(transport
);
120 static CF_RETURNS_RETAINED CFDictionaryRef
handleMessages(SOSTransportMessageRef transport
, CFMutableDictionaryRef circle_peer_messages_table
, CFErrorRef
*error
) {
121 // TODO: This might need to be: return CFDictionaryCreateForCFTypes(kCFAllocatorDefault, NULL);
122 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault
);
125 HandleIDSMessageReason
SOSTransportMessageIDSHandleMessage(SOSAccountRef account
, CFDictionaryRef message
, CFErrorRef
*error
) {
127 secdebug("IDS Transport", "SOSTransportMessageIDSHandleMessage!");
129 CFStringRef dataKey
= CFStringCreateWithCString(kCFAllocatorDefault
, kMessageKeyIDSDataMessage
, kCFStringEncodingASCII
);
130 CFStringRef deviceIDKey
= CFStringCreateWithCString(kCFAllocatorDefault
, kMessageKeyDeviceID
, kCFStringEncodingASCII
);
132 CFDataRef messageData
= (CFDataRef
)CFDictionaryGetValue(message
, dataKey
);
133 CFStringRef fromID
= (CFStringRef
)CFDictionaryGetValue(message
, deviceIDKey
);
135 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(account
);
138 CFReleaseNull(deviceIDKey
);
139 CFReleaseNull(dataKey
);
140 if(!SOSAccountHasFullPeerInfo(account
, error
))
141 return kHandleIDSMessageOtherFail
;
144 __block CFStringRef peerID
= NULL
;
146 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
147 CFStringRef deviceID
= SOSPeerInfoCopyDeviceID(peer
);
148 if(CFStringCompare(deviceID
, fromID
, 0) == 0)
149 peerID
= SOSPeerInfoGetPeerID(peer
);
150 CFReleaseNull(deviceID
);
153 secerror("Could not find peer matching the IDS device ID, dropping message");
154 CFReleaseNull(dataKey
);
155 CFReleaseNull(deviceIDKey
);
156 return kHandleIDSMessageNotReady
;
161 if (SOSTransportMessageHandlePeerMessage(account
->ids_message_transport
, peerID
, messageData
, error
)) {
162 CFMutableDictionaryRef peersToSyncWith
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
163 CFMutableArrayRef peerIDs
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
164 CFArrayAppendValue(peerIDs
, peerID
);
165 CFDictionaryAddValue(peersToSyncWith
, SOSCircleGetName(account
->trusted_circle
), peerIDs
);
167 if(!SOSTransportMessageSyncWithPeers(account
->ids_message_transport
, peersToSyncWith
, error
))
169 secerror("SOSTransportMessageIDSHandleMessage Could not sync with all peers: %@", *error
);
172 secdebug("IDS Transport", "Synced with all peers!");
173 CFReleaseNull(dataKey
);
174 CFReleaseNull(deviceIDKey
);
175 return kHandleIDSMessageSuccess
;
180 CFReleaseNull(dataKey
);
181 CFReleaseNull(deviceIDKey
);
182 secerror("IDS Transport Could not handle message: %@", messageData
);
183 return kHandleIDSMessageOtherFail
;
186 secerror("Data doesn't exist: %@", messageData
);
187 CFReleaseNull(deviceIDKey
);
188 CFReleaseNull(dataKey
);
189 return kHandleIDSMessageOtherFail
;
193 static bool sendToPeer(SOSTransportMessageRef transport
, CFStringRef circleName
, CFStringRef deviceID
, CFStringRef peerID
, idsOperation whichOTRType
, CFDataRef message
, CFErrorRef
*error
)
195 __block
bool success
= false;
196 CFStringRef errorMessage
= NULL
;
197 CFDictionaryRef userInfo
;
198 CFStringRef operation
= NULL
;
199 CFDataRef operationData
= NULL
;
200 CFMutableDataRef mutableData
= NULL
;
201 SOSAccountRef account
= SOSTransportMessageGetAccount(transport
);
202 CFStringRef ourPeerID
= SOSPeerInfoGetPeerID(SOSAccountGetMyPeerInfo(account
));
204 require_action_quiet((deviceID
!= NULL
&& CFStringGetLength(deviceID
) >0), fail
, errorMessage
= CFSTR("Need an IDS Device ID to sync"));
206 if(whichOTRType
== kIDSSyncMessagesCompact
)
207 operation
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%d"), kIDSSyncMessagesCompact
);
209 operation
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%d"), kIDSSyncMessagesRaw
);
211 require_action_quiet(operation
, fail
, errorMessage
= CFSTR("Failed to allocate a CFStringRef"));
213 operationData
= CFStringCreateExternalRepresentation(kCFAllocatorDefault
, operation
, kCFStringEncodingUTF8
, 0);
214 require_action_quiet(operationData
, fail
, errorMessage
= CFSTR("Failed to allocate data"));
216 mutableData
= CFDataCreateMutable(kCFAllocatorDefault
, CFDataGetLength(operationData
) + CFDataGetLength(message
));
217 require_action_quiet(mutableData
, fail
, errorMessage
= CFSTR("Failed to allocate mutable data"));
219 CFDataAppend(mutableData
, operationData
);
220 CFDataAppend(mutableData
, message
);
222 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
223 dispatch_retain(wait_for
); // Both this scope and the block own it.
225 secnotice("ids transport", "Starting");
227 SOSCloudKeychainSendIDSMessage(mutableData
, deviceID
, ourPeerID
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
) {
228 success
= (sync_error
== NULL
);
230 CFRetainAssign(*error
, sync_error
);
233 dispatch_semaphore_signal(wait_for
);
234 dispatch_release(wait_for
);
237 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
238 dispatch_release(wait_for
);
242 secerror("Failed to send message to peer! %@", *error
);
244 secerror("Failed to send message to peer");
247 secdebug("IDS Transport", "Sent message to peer!");
250 CFReleaseNull(operation
);
251 CFReleaseNull(operationData
);
252 CFReleaseNull(mutableData
);
257 userInfo
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, kCFErrorLocalizedDescriptionKey
, errorMessage
, NULL
);
259 *error
=CFErrorCreate(kCFAllocatorDefault
, CFSTR("com.apple.security.ids.error"), kSecIDSErrorNoDeviceID
, userInfo
);
260 secerror("%@", *error
);
262 CFReleaseNull(operation
);
263 CFReleaseNull(operationData
);
264 CFReleaseNull(mutableData
);
265 CFReleaseNull(userInfo
);
270 static bool syncWithPeers(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeerIDs
, CFErrorRef
*error
) {
271 // Each entry is keyed by circle name and contains a list of peerIDs
272 __block
bool result
= true;
274 CFDictionaryForEach(circleToPeerIDs
, ^(const void *key
, const void *value
) {
275 if (isString(key
) && isArray(value
)) {
276 CFStringRef circleName
= (CFStringRef
) key
;
277 CFArrayForEach(value
, ^(const void *value
) {
278 if (isString(value
)) {
279 CFStringRef peerID
= (CFStringRef
) value
;
280 result
&= SOSTransportMessageSendMessageIfNeeded(transport
, circleName
, peerID
, error
);
289 static bool sendMessages(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeersToMessage
, CFErrorRef
*error
) {
290 __block
bool result
= true;
291 SOSCircleRef circle
= SOSAccountGetCircle(transport
->account
, error
);
292 __block SOSAccountRef account
= SOSTransportMessageGetAccount(transport
);
293 __block SOSPeerInfoRef myPeerInfo
= SOSFullPeerInfoGetPeerInfo(SOSAccountGetMyFullPeerInfo(account
));
295 CFDictionaryForEach(circleToPeersToMessage
, ^(const void *key
, const void *value
) {
296 if (isString(key
) && isDictionary(value
)) {
297 CFStringRef circleName
= (CFStringRef
) key
;
298 CFDictionaryForEach(value
, ^(const void *key
, const void *value
) {
299 if (isString(key
) && isData(value
)) {
300 CFStringRef peerID
= (CFStringRef
) key
;
301 CFDataRef message
= (CFDataRef
) value
;
302 SOSCircleForEachPeer(circle
, ^(SOSPeerInfoRef peer
) {
303 CFStringRef deviceID
= SOSPeerInfoCopyDeviceID(peer
);
304 if(CFEqualSafe(SOSPeerInfoGetPeerID(peer
), peerID
) || CFEqualSafe(deviceID
, peerID
)){
306 if(SOSPeerInfoShouldUseIDSTransport(myPeerInfo
, peer
))
307 rx
= sendToPeer(transport
, circleName
, deviceID
, peerID
, kIDSSyncMessagesCompact
, message
, error
);
309 rx
= sendToPeer(transport
, circleName
, deviceID
, peerID
, kIDSSyncMessagesRaw
, message
, error
);
313 CFReleaseNull(deviceID
);
323 static bool flushChanges(SOSTransportMessageRef transport
, CFErrorRef
*error
)
328 static bool cleanupAfterPeer(SOSTransportMessageRef transport
, CFDictionaryRef circle_to_peer_ids
, CFErrorRef
*error
)