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>
22 #include <Security/SecureObjectSync/SOSInternal.h>
24 #define IDS "IDS transport"
26 struct __OpaqueSOSTransportMessageIDS
{
27 struct __OpaqueSOSTransportMessage m
;
28 CFBooleanRef useFragmentation
;
32 // V-table implementation forward declarations
34 static bool sendToPeer(SOSTransportMessageRef transport
, bool shouldUseAckModel
, CFStringRef circleName
, CFStringRef deviceID
, CFStringRef peerID
,CFDictionaryRef message
, CFErrorRef
*error
);
35 static bool syncWithPeers(SOSTransportMessageRef transport
, CFSetRef peers
, CFErrorRef
*error
);
36 static bool sendMessages(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeersToMessage
, CFErrorRef
*error
);
37 static void destroy(SOSTransportMessageRef transport
);
38 static bool cleanupAfterPeer(SOSTransportMessageRef transport
, CFDictionaryRef circle_to_peer_ids
, CFErrorRef
*error
);
39 static bool flushChanges(SOSTransportMessageRef transport
, CFErrorRef
*error
);
40 static CF_RETURNS_RETAINED CFDictionaryRef
handleMessages(SOSTransportMessageRef transport
, CFMutableDictionaryRef circle_peer_messages_table
, CFErrorRef
*error
);
42 static inline CFIndex
getTransportType(SOSTransportMessageRef transport
, CFErrorRef
*error
){
46 void SOSTransportMessageIDSSetFragmentationPreference(SOSTransportMessageRef transport
, CFBooleanRef preference
){
47 SOSTransportMessageIDSRef t
= (SOSTransportMessageIDSRef
)transport
;
48 t
->useFragmentation
= preference
;
51 CFBooleanRef
SOSTransportMessageIDSGetFragmentationPreference(SOSTransportMessageRef transport
){
52 SOSTransportMessageIDSRef t
= (SOSTransportMessageIDSRef
)transport
;
53 return t
->useFragmentation
;
56 SOSTransportMessageIDSRef
SOSTransportMessageIDSCreate(SOSAccountRef account
, CFStringRef circleName
, CFErrorRef
*error
)
58 SOSTransportMessageIDSRef ids
= (SOSTransportMessageIDSRef
) SOSTransportMessageCreateForSubclass(sizeof(struct __OpaqueSOSTransportMessageIDS
) - sizeof(CFRuntimeBase
), account
, circleName
, error
);
62 ids
->m
.sendMessages
= sendMessages
;
63 ids
->m
.syncWithPeers
= syncWithPeers
;
64 ids
->m
.flushChanges
= flushChanges
;
65 ids
->m
.cleanupAfterPeerMessages
= cleanupAfterPeer
;
66 ids
->m
.destroy
= destroy
;
67 ids
->m
.handleMessages
= handleMessages
;
68 ids
->m
.getTransportType
= getTransportType
;
70 // Initialize ourselves
72 SOSTransportMessageIDSGetIDSDeviceID(account
);
73 SOSRegisterTransportMessage((SOSTransportMessageRef
)ids
);
78 static void destroy(SOSTransportMessageRef transport
){
79 SOSUnregisterTransportMessage(transport
);
82 static CF_RETURNS_RETAINED CFDictionaryRef
handleMessages(SOSTransportMessageRef transport
, CFMutableDictionaryRef circle_peer_messages_table
, CFErrorRef
*error
) {
83 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, NULL
);
86 static HandleIDSMessageReason
checkMessageValidity(SOSAccountRef account
, CFStringRef fromDeviceID
, CFStringRef fromPeerID
, CFStringRef
*peerID
, SOSPeerInfoRef
*theirPeerInfo
){
88 __block HandleIDSMessageReason reason
= kHandleIDSMessageDontHandle
;
90 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
91 CFStringRef deviceID
= SOSPeerInfoCopyDeviceID(peer
);
92 CFStringRef pID
= SOSPeerInfoGetPeerID(peer
);
94 if( deviceID
&& pID
&& fromPeerID
&& fromDeviceID
&& CFStringGetLength(fromPeerID
) != 0 ){
95 if(CFStringCompare(pID
, fromPeerID
, 0) == 0){
96 if(CFStringGetLength(deviceID
) == 0){
97 secnotice("ids transport", "device ID was empty in the peer list, holding on to message");
98 CFReleaseNull(deviceID
);
99 reason
= kHandleIDSMessageNotReady
;
102 else if(CFStringCompare(fromDeviceID
, deviceID
, 0) != 0){ //IDSids do not match, ghost
103 reason
= kHandleIDSmessageDeviceIDMismatch
;
104 CFReleaseNull(deviceID
);
107 else if(CFStringCompare(deviceID
, fromDeviceID
, 0) == 0){
109 *theirPeerInfo
= peer
;
110 CFReleaseNull(deviceID
);
111 reason
= kHandleIDSMessageSuccess
;
116 CFReleaseNull(deviceID
);
122 HandleIDSMessageReason
SOSTransportMessageIDSHandleMessage(SOSAccountRef account
, CFDictionaryRef message
, CFErrorRef
*error
) {
124 secnotice("IDS Transport", "SOSTransportMessageIDSHandleMessage!");
126 CFStringRef dataKey
= CFStringCreateWithCString(kCFAllocatorDefault
, kMessageKeyIDSDataMessage
, kCFStringEncodingASCII
);
127 CFStringRef deviceIDKey
= CFStringCreateWithCString(kCFAllocatorDefault
, kMessageKeyDeviceID
, kCFStringEncodingASCII
);
128 CFStringRef sendersPeerIDKey
= CFStringCreateWithCString(kCFAllocatorDefault
, kMessageKeySendersPeerID
, kCFStringEncodingASCII
);
129 CFStringRef ourPeerIdKey
= CFStringCreateWithCString(kCFAllocatorDefault
, kMessageKeyPeerID
, kCFStringEncodingASCII
);
131 HandleIDSMessageReason result
= kHandleIDSMessageSuccess
;
133 CFDataRef messageData
= asData(CFDictionaryGetValue(message
, dataKey
), NULL
);
134 __block CFStringRef fromDeviceID
= asString(CFDictionaryGetValue(message
, deviceIDKey
), NULL
);
135 __block CFStringRef fromPeerID
= (CFStringRef
)CFDictionaryGetValue(message
, sendersPeerIDKey
);
136 CFStringRef ourPeerID
= asString(CFDictionaryGetValue(message
, ourPeerIdKey
), NULL
);
138 CFStringRef peerID
= NULL
;
139 SOSPeerInfoRef theirPeer
= NULL
;
141 require_action_quiet(fromDeviceID
, exit
, result
= kHandleIDSMessageDontHandle
);
142 require_action_quiet(fromPeerID
, exit
, result
= kHandleIDSMessageDontHandle
);
143 require_action_quiet(messageData
&& CFDataGetLength(messageData
) != 0, exit
, result
= kHandleIDSMessageDontHandle
);
144 require_action_quiet(SOSAccountHasFullPeerInfo(account
, error
), exit
, result
= kHandleIDSMessageNotReady
);
145 require_action_quiet(ourPeerID
&& CFEqualSafe(ourPeerID
, SOSAccountGetMyPeerID(account
)), exit
, result
= kHandleIDSMessageDontHandle
; secnotice("IDS Transport","ignoring message for: %@", ourPeerID
));
147 require_quiet((result
= checkMessageValidity( account
, fromDeviceID
, fromPeerID
, &peerID
, &theirPeer
)) == kHandleIDSMessageSuccess
, exit
);
149 if (SOSTransportMessageHandlePeerMessage(account
->ids_message_transport
, peerID
, messageData
, error
)) {
150 CFMutableDictionaryRef peersToSyncWith
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
151 CFMutableSetRef peerIDs
= CFSetCreateMutableForCFTypes(kCFAllocatorDefault
);
152 CFSetAddValue(peerIDs
, peerID
);
154 //sync using fragmentation?
155 if(SOSPeerInfoShouldUseIDSMessageFragmentation(SOSFullPeerInfoGetPeerInfo(account
->my_identity
), theirPeer
)){
156 //set useFragmentation bit
157 SOSTransportMessageIDSSetFragmentationPreference(account
->ids_message_transport
, kCFBooleanTrue
);
160 SOSTransportMessageIDSSetFragmentationPreference(account
->ids_message_transport
, kCFBooleanFalse
);
163 if(!SOSTransportMessageSyncWithPeers(account
->ids_message_transport
, peerIDs
, error
)){
164 secerror("SOSTransportMessageIDSHandleMessage Could not sync with all peers: %@", *error
);
166 secnotice("IDS Transport", "Synced with all peers!");
169 CFReleaseNull(peersToSyncWith
);
170 CFReleaseNull(peerIDs
);
172 if(error
&& *error
!= NULL
){
173 CFStringRef errorMessage
= CFErrorCopyDescription(*error
);
174 if (-25308 == CFErrorGetCode(*error
)) { // tell KeychainSyncingOverIDSProxy to call us back when device unlocks
175 result
= kHandleIDSMessageLocked
;
176 }else{ //else drop it, couldn't handle the message
177 result
= kHandleIDSMessageDontHandle
;
179 secerror("IDS Transport Could not handle message: %@, %@", messageData
, *error
);
180 CFReleaseNull(errorMessage
);
183 else{ //no error but failed? drop it, log message
184 secerror("IDS Transport Could not handle message: %@", messageData
);
185 result
= kHandleIDSMessageDontHandle
;
191 CFReleaseNull(ourPeerIdKey
);
192 CFReleaseNull(sendersPeerIDKey
);
193 CFReleaseNull(deviceIDKey
);
194 CFReleaseNull(dataKey
);
199 static bool sendToPeer(SOSTransportMessageRef transport
, bool shouldUseAckModel
, CFStringRef circleName
, CFStringRef deviceID
, CFStringRef peerID
,CFDictionaryRef message
, CFErrorRef
*error
)
201 __block
bool success
= false;
202 CFStringRef errorMessage
= NULL
;
203 CFDictionaryRef userInfo
;
204 CFStringRef operation
= NULL
;
205 CFDataRef operationData
= NULL
;
206 CFMutableDataRef mutableData
= NULL
;
207 SOSAccountRef account
= SOSTransportMessageGetAccount(transport
);
208 CFStringRef ourPeerID
= SOSPeerInfoGetPeerID(SOSAccountGetMyPeerInfo(account
));
209 CFStringRef operationToString
= NULL
;
211 CFDictionaryRef messagetoSend
= NULL
;
213 require_action_quiet((deviceID
!= NULL
&& CFStringGetLength(deviceID
) >0), fail
, errorMessage
= CFSTR("Need an IDS Device ID to sync"));
215 if(CFDictionaryGetValue(message
, kIDSOperationType
) == NULL
&& SOSTransportMessageIDSGetFragmentationPreference(transport
) == kCFBooleanTrue
){
216 //handle a keychain data blob using fragmentation!
217 secnotice("IDS Transport","sendToPeer: using fragmentation!");
219 operationToString
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%d"), kIDSKeychainSyncIDSFragmentation
);
220 messagetoSend
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
221 kIDSOperationType
, operationToString
,
222 kIDSMessageRecipientDeviceID
, deviceID
,
223 kIDSMessageRecipientPeerID
, peerID
,
224 kIDSMessageUsesAckModel
, (shouldUseAckModel
? CFSTR("YES") : CFSTR("NO")),
225 kIDSMessageToSendKey
, message
,
228 else{ //otherhandle handle the test message without fragmentation
229 secnotice("IDS Transport","sendToPeer: not going to fragment message");
231 CFMutableDictionaryRef annotatedMessage
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, message
);
232 CFDictionaryAddValue(annotatedMessage
, kIDSMessageRecipientPeerID
, peerID
);
233 CFDictionaryAddValue(annotatedMessage
, kIDSMessageRecipientDeviceID
, deviceID
);
234 CFDictionaryAddValue(annotatedMessage
, kIDSMessageUsesAckModel
, (shouldUseAckModel
? CFSTR("YES") : CFSTR("NO")));
235 CFTransferRetained(messagetoSend
, annotatedMessage
);
236 CFReleaseNull(annotatedMessage
);
239 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
240 dispatch_retain(wait_for
); // Both this scope and the block own it.
242 secnotice("ids transport", "Starting");
244 SOSCloudKeychainSendIDSMessage(messagetoSend
, deviceID
, ourPeerID
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), SOSTransportMessageIDSGetFragmentationPreference(transport
), ^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
) {
245 success
= (sync_error
== NULL
);
246 if (sync_error
&& error
) {
247 CFRetainAssign(*error
, sync_error
);
250 dispatch_semaphore_signal(wait_for
);
251 dispatch_release(wait_for
);
254 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
255 dispatch_release(wait_for
);
259 secerror("Failed to send message to peer! %@", *error
);
261 secerror("Failed to send message to peer");
264 secnotice("IDS Transport", "Sent message to peer!");
267 CFReleaseNull(messagetoSend
);
268 CFReleaseNull(operation
);
269 CFReleaseNull(operationData
);
270 CFReleaseNull(mutableData
);
271 CFReleaseNull(operationToString
);
275 userInfo
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, kCFErrorLocalizedDescriptionKey
, errorMessage
, NULL
);
277 *error
=CFErrorCreate(kCFAllocatorDefault
, CFSTR("com.apple.security.ids.error"), kSecIDSErrorNoDeviceID
, userInfo
);
278 secerror("%@", *error
);
280 CFReleaseNull(messagetoSend
);
281 CFReleaseNull(operation
);
282 CFReleaseNull(operationData
);
283 CFReleaseNull(mutableData
);
284 CFReleaseNull(userInfo
);
285 CFReleaseNull(operationToString
);
291 static bool syncWithPeers(SOSTransportMessageRef transport
, CFSetRef peers
, CFErrorRef
*error
) {
292 // Each entry is keyed by circle name and contains a list of peerIDs
293 __block
bool result
= true;
295 CFSetForEach(peers
, ^(const void *value
) {
296 CFStringRef peerID
= asString(value
, NULL
);
297 result
&= SOSTransportMessageSendMessageIfNeeded(transport
, transport
->circleName
, peerID
, error
);
303 static bool sendMessages(SOSTransportMessageRef transport
, CFDictionaryRef peersToMessage
, CFErrorRef
*error
) {
304 __block
bool result
= true;
306 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(transport
->account
);
307 CFStringRef myID
= SOSPeerInfoGetPeerID(myPeer
);
308 require_quiet(myPeer
, fail
);
310 CFStringRef circleName
= transport
->circleName
;
312 CFDictionaryForEach(peersToMessage
, ^(const void *key
, const void *value
) {
313 CFErrorRef error
= NULL
;
315 SOSPeerInfoRef peer
= NULL
;
316 CFStringRef deviceID
= NULL
;
317 CFDictionaryRef message
= NULL
;
319 CFStringRef peerID
= asString(key
, &error
);
320 require_quiet(peerID
, skip
);
321 require_quiet(!CFEqualSafe(myID
, key
), skip
);
323 message
= CFRetainSafe(asDictionary(value
, &error
));
324 if (message
== NULL
) {
325 // If it's not a data, return the error
326 CFDataRef messageData
= asData(value
, NULL
);
328 CFReleaseNull(error
);
329 message
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, peerID
, messageData
, NULL
);
332 require_quiet(message
, skip
);
334 peer
= SOSAccountCopyPeerWithID(transport
->account
, peerID
, &error
);
335 require_quiet(peer
, skip
);
337 deviceID
= SOSPeerInfoCopyDeviceID(peer
);
338 require_action_quiet(deviceID
, skip
, SOSErrorCreate(kSOSErrorSendFailure
, &error
, NULL
, CFSTR("No IDS ID")));
340 SOSTransportMessageIDSSetFragmentationPreference(transport
,
341 SOSPeerInfoShouldUseIDSMessageFragmentation(myPeer
, peer
) ? kCFBooleanTrue
: kCFBooleanFalse
);
342 bool shouldUseAckModel
= SOSPeerInfoShouldUseACKModel(myPeer
, peer
);
344 result
&= sendToPeer(transport
, shouldUseAckModel
, circleName
, deviceID
, peerID
, message
, &error
);
348 secerror("Failed to sync to %@ over IDS: %@", peerID
, error
);
352 CFReleaseNull(deviceID
);
353 CFReleaseNull(message
);
355 CFReleaseNull(error
);
363 static bool flushChanges(SOSTransportMessageRef transport
, CFErrorRef
*error
)
368 static bool cleanupAfterPeer(SOSTransportMessageRef transport
, CFDictionaryRef circle_to_peer_ids
, CFErrorRef
*error
)
373 bool SOSTransportMessageIDSGetIDSDeviceID(SOSAccountRef account
){
374 CFStringRef deviceID
= SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
375 bool hasDeviceID
= deviceID
!= NULL
&& CFStringGetLength(deviceID
) != 0;
376 CFReleaseNull(deviceID
);
379 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
){
380 bool success
= (sync_error
== NULL
);
382 secerror("Could not ask KeychainSyncingOverIDSProxy for Device ID: %@", sync_error
);
385 secnotice("IDS Transport", "Successfully attempting to retrieve the IDS Device ID");