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
;
28 CFBooleanRef useFragmentation
;
31 const CFStringRef kSecIDSErrorDomain
= CFSTR("com.apple.security.ids.error");
32 const CFStringRef kIDSOperationType
= CFSTR("IDSMessageOperation");
33 const CFStringRef kIDSMessageToSendKey
= CFSTR("MessageToSendKey");
36 // V-table implementation forward declarations
38 static bool sendToPeer(SOSTransportMessageRef transport
, CFStringRef circleName
, CFStringRef deviceID
, CFStringRef peerID
,CFDictionaryRef message
, CFErrorRef
*error
);
39 static bool syncWithPeers(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeerIDs
, CFErrorRef
*error
);
40 static bool sendMessages(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeersToMessage
, CFErrorRef
*error
);
41 static void destroy(SOSTransportMessageRef transport
);
42 static bool cleanupAfterPeer(SOSTransportMessageRef transport
, CFDictionaryRef circle_to_peer_ids
, CFErrorRef
*error
);
43 static bool flushChanges(SOSTransportMessageRef transport
, CFErrorRef
*error
);
44 static CF_RETURNS_RETAINED CFDictionaryRef
handleMessages(SOSTransportMessageRef transport
, CFMutableDictionaryRef circle_peer_messages_table
, CFErrorRef
*error
);
46 static inline CFIndex
getTransportType(SOSTransportMessageRef transport
, CFErrorRef
*error
){
50 void SOSTransportMessageIDSSetFragmentationPreference(SOSTransportMessageRef transport
, CFBooleanRef preference
){
51 SOSTransportMessageIDSRef t
= (SOSTransportMessageIDSRef
)transport
;
52 t
->useFragmentation
= preference
;
55 CFBooleanRef
SOSTransportMessageIDSGetFragmentationPreference(SOSTransportMessageRef transport
){
56 SOSTransportMessageIDSRef t
= (SOSTransportMessageIDSRef
)transport
;
57 return t
->useFragmentation
;
60 SOSTransportMessageIDSRef
SOSTransportMessageIDSCreate(SOSAccountRef account
, CFStringRef circleName
, CFErrorRef
*error
)
62 SOSTransportMessageIDSRef ids
= (SOSTransportMessageIDSRef
) SOSTransportMessageCreateForSubclass(sizeof(struct __OpaqueSOSTransportMessageIDS
) - sizeof(CFRuntimeBase
), account
, circleName
, error
);
66 ids
->m
.sendMessages
= sendMessages
;
67 ids
->m
.syncWithPeers
= syncWithPeers
;
68 ids
->m
.flushChanges
= flushChanges
;
69 ids
->m
.cleanupAfterPeerMessages
= cleanupAfterPeer
;
70 ids
->m
.destroy
= destroy
;
71 ids
->m
.handleMessages
= handleMessages
;
72 ids
->m
.getTransportType
= getTransportType
;
74 // Initialize ourselves
76 SOSTransportMessageIDSGetIDSDeviceID(account
);
77 SOSRegisterTransportMessage((SOSTransportMessageRef
)ids
);
82 static void destroy(SOSTransportMessageRef transport
){
83 SOSUnregisterTransportMessage(transport
);
86 static CF_RETURNS_RETAINED CFDictionaryRef
handleMessages(SOSTransportMessageRef transport
, CFMutableDictionaryRef circle_peer_messages_table
, CFErrorRef
*error
) {
87 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, NULL
);
90 static HandleIDSMessageReason
checkMessageValidity(SOSAccountRef account
, CFStringRef fromDeviceID
, CFStringRef fromPeerID
, CFStringRef
*peerID
, SOSPeerInfoRef
*theirPeerInfo
){
92 __block HandleIDSMessageReason reason
= kHandleIDSMessageDontHandle
;
94 SOSCircleForEachPeer(account
->trusted_circle
, ^(SOSPeerInfoRef peer
) {
95 CFStringRef deviceID
= SOSPeerInfoCopyDeviceID(peer
);
96 CFStringRef pID
= SOSPeerInfoGetPeerID(peer
);
98 if( deviceID
&& pID
&& fromPeerID
&& fromDeviceID
&& CFStringGetLength(fromPeerID
) != 0 ){
99 if(CFStringCompare(pID
, fromPeerID
, 0) == 0){
100 if(CFStringGetLength(deviceID
) == 0){
101 secnotice("ids transport", "device ID was empty in the peer list, holding on to message");
102 CFReleaseNull(deviceID
);
103 reason
= kHandleIDSMessageNotReady
;
106 else if(CFStringCompare(fromDeviceID
, deviceID
, 0) != 0){ //IDSids do not match, ghost
107 reason
= kHandleIDSmessageDeviceIDMismatch
;
108 CFReleaseNull(deviceID
);
111 else if(CFStringCompare(deviceID
, fromDeviceID
, 0) == 0){
113 *theirPeerInfo
= peer
;
114 CFReleaseNull(deviceID
);
115 reason
= kHandleIDSMessageSuccess
;
120 CFReleaseNull(deviceID
);
126 HandleIDSMessageReason
SOSTransportMessageIDSHandleMessage(SOSAccountRef account
, CFDictionaryRef message
, CFErrorRef
*error
) {
128 secnotice("IDS Transport", "SOSTransportMessageIDSHandleMessage!");
130 CFStringRef dataKey
= CFStringCreateWithCString(kCFAllocatorDefault
, kMessageKeyIDSDataMessage
, kCFStringEncodingASCII
);
131 CFStringRef deviceIDKey
= CFStringCreateWithCString(kCFAllocatorDefault
, kMessageKeyDeviceID
, kCFStringEncodingASCII
);
132 CFStringRef sendersPeerIDKey
= CFStringCreateWithCString(kCFAllocatorDefault
, kMessageKeySendersPeerID
, kCFStringEncodingASCII
);
134 HandleIDSMessageReason result
= kHandleIDSMessageSuccess
;
136 CFDataRef messageData
= asData(CFDictionaryGetValue(message
, dataKey
), NULL
);
137 __block CFStringRef fromDeviceID
= asString(CFDictionaryGetValue(message
, deviceIDKey
), NULL
);
138 __block CFStringRef fromPeerID
= (CFStringRef
)CFDictionaryGetValue(message
, sendersPeerIDKey
);
140 CFStringRef peerID
= NULL
;
141 SOSPeerInfoRef theirPeer
= NULL
;
143 require_action_quiet(fromDeviceID
, exit
, result
= kHandleIDSMessageDontHandle
);
144 require_action_quiet(fromPeerID
, exit
, result
= kHandleIDSMessageDontHandle
);
145 require_action_quiet(messageData
&& CFDataGetLength(messageData
) != 0, exit
, result
= kHandleIDSMessageDontHandle
);
146 require_action_quiet(SOSAccountHasFullPeerInfo(account
, error
), exit
, result
= kHandleIDSMessageNotReady
);
148 require_quiet((result
= checkMessageValidity( account
, fromDeviceID
, fromPeerID
, &peerID
, &theirPeer
)) == kHandleIDSMessageSuccess
, exit
);
150 if (SOSTransportMessageHandlePeerMessage(account
->ids_message_transport
, peerID
, messageData
, error
)) {
151 CFMutableDictionaryRef peersToSyncWith
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
152 CFMutableArrayRef peerIDs
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
153 CFArrayAppendValue(peerIDs
, peerID
);
154 CFDictionaryAddValue(peersToSyncWith
, SOSCircleGetName(account
->trusted_circle
), peerIDs
);
156 //sync using fragmentation?
157 if(SOSPeerInfoShouldUseIDSMessageFragmentation(SOSFullPeerInfoGetPeerInfo(account
->my_identity
), theirPeer
)){
158 //set useFragmentation bit
159 SOSTransportMessageIDSSetFragmentationPreference(account
->ids_message_transport
, kCFBooleanTrue
);
162 SOSTransportMessageIDSSetFragmentationPreference(account
->ids_message_transport
, kCFBooleanFalse
);
165 if(!SOSTransportMessageSyncWithPeers(account
->ids_message_transport
, peersToSyncWith
, error
)){
166 secerror("SOSTransportMessageIDSHandleMessage Could not sync with all peers: %@", *error
);
168 secnotice("IDS Transport", "Synced with all peers!");
171 CFReleaseNull(peersToSyncWith
);
172 CFReleaseNull(peerIDs
);
174 if(error
&& *error
!= NULL
){
175 CFStringRef errorMessage
= CFErrorCopyDescription(*error
);
176 if (-25308 == CFErrorGetCode(*error
)) { // tell IDSKeychainSyncingProxy to call us back when device unlocks
177 result
= kHandleIDSMessageLocked
;
178 }else{ //else drop it, couldn't handle the message
179 result
= kHandleIDSMessageDontHandle
;
181 secerror("IDS Transport Could not handle message: %@, %@", messageData
, *error
);
182 CFReleaseNull(errorMessage
);
185 else{ //no error but failed? drop it, log message
186 secerror("IDS Transport Could not handle message: %@", messageData
);
187 result
= kHandleIDSMessageDontHandle
;
193 CFReleaseNull(sendersPeerIDKey
);
194 CFReleaseNull(deviceIDKey
);
195 CFReleaseNull(dataKey
);
200 static bool sendToPeer(SOSTransportMessageRef transport
, CFStringRef circleName
, CFStringRef deviceID
, CFStringRef peerID
,CFDictionaryRef message
, CFErrorRef
*error
)
202 __block
bool success
= false;
203 CFStringRef errorMessage
= NULL
;
204 CFDictionaryRef userInfo
;
205 CFStringRef operation
= NULL
;
206 CFDataRef operationData
= NULL
;
207 CFMutableDataRef mutableData
= NULL
;
208 SOSAccountRef account
= SOSTransportMessageGetAccount(transport
);
209 CFStringRef ourPeerID
= SOSPeerInfoGetPeerID(SOSAccountGetMyPeerInfo(account
));
210 CFStringRef operationToString
= NULL
;
212 CFDictionaryRef messagetoSend
= NULL
;
214 require_action_quiet((deviceID
!= NULL
&& CFStringGetLength(deviceID
) >0), fail
, errorMessage
= CFSTR("Need an IDS Device ID to sync"));
216 if(CFDictionaryGetValue(message
, kIDSOperationType
) == NULL
&& SOSTransportMessageIDSGetFragmentationPreference(transport
) == kCFBooleanTrue
){
217 //otherwise handle a keychain data blob using fragmentation!
218 secnotice("IDS Transport","sendToPeer: using fragmentation!");
219 char *messageCharStar
;
220 asprintf(&messageCharStar
, "%d", kIDSKeychainSyncIDSFragmentation
);
221 operationToString
= CFStringCreateWithCString(kCFAllocatorDefault
, messageCharStar
, kCFStringEncodingUTF8
);
222 free(messageCharStar
);
224 messagetoSend
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, kIDSOperationType
, operationToString
, kIDSMessageToSendKey
, message
, NULL
);
226 else{ //otherhandle handle the test message without fragmentation
227 messagetoSend
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, CFDictionaryGetCount(message
), message
);
228 secnotice("IDS Transport","sendToPeer: not going to fragment message");
231 dispatch_semaphore_t wait_for
= dispatch_semaphore_create(0);
232 dispatch_retain(wait_for
); // Both this scope and the block own it.
234 secnotice("ids transport", "Starting");
236 SOSCloudKeychainSendIDSMessage(messagetoSend
, deviceID
, ourPeerID
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), SOSTransportMessageIDSGetFragmentationPreference(transport
), ^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
) {
237 success
= (sync_error
== NULL
);
238 if (sync_error
&& error
) {
239 CFRetainAssign(*error
, sync_error
);
242 dispatch_semaphore_signal(wait_for
);
243 dispatch_release(wait_for
);
246 dispatch_semaphore_wait(wait_for
, DISPATCH_TIME_FOREVER
);
247 dispatch_release(wait_for
);
251 secerror("Failed to send message to peer! %@", *error
);
253 secerror("Failed to send message to peer");
256 secnotice("IDS Transport", "Sent message to peer!");
259 CFReleaseNull(messagetoSend
);
260 CFReleaseNull(operation
);
261 CFReleaseNull(operationData
);
262 CFReleaseNull(mutableData
);
263 CFReleaseNull(operationToString
);
267 userInfo
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
, kCFErrorLocalizedDescriptionKey
, errorMessage
, NULL
);
269 *error
=CFErrorCreate(kCFAllocatorDefault
, CFSTR("com.apple.security.ids.error"), kSecIDSErrorNoDeviceID
, userInfo
);
270 secerror("%@", *error
);
272 CFReleaseNull(messagetoSend
);
273 CFReleaseNull(operation
);
274 CFReleaseNull(operationData
);
275 CFReleaseNull(mutableData
);
276 CFReleaseNull(userInfo
);
277 CFReleaseNull(operationToString
);
283 static bool syncWithPeers(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeerIDs
, CFErrorRef
*error
) {
284 // Each entry is keyed by circle name and contains a list of peerIDs
285 __block
bool result
= true;
287 CFDictionaryForEach(circleToPeerIDs
, ^(const void *key
, const void *value
) {
288 if (isString(key
) && isArray(value
)) {
289 CFStringRef circleName
= (CFStringRef
) key
;
290 CFArrayForEach(value
, ^(const void *value
) {
291 if (isString(value
)) {
292 CFStringRef peerID
= (CFStringRef
) value
;
293 result
&= SOSTransportMessageSendMessageIfNeeded(transport
, circleName
, peerID
, error
);
302 static bool sendMessages(SOSTransportMessageRef transport
, CFDictionaryRef circleToPeersToMessage
, CFErrorRef
*error
) {
303 __block
bool result
= true;
304 SOSCircleRef circle
= SOSAccountGetCircle(transport
->account
, error
);
305 SOSPeerInfoRef myPeer
= SOSAccountGetMyPeerInfo(transport
->account
);
306 __block CFDictionaryRef message
= NULL
;
307 __block CFStringRef peerID
= NULL
;
308 require_quiet(myPeer
, fail
);
311 CFDictionaryForEach(circleToPeersToMessage
, ^(const void *key
, const void *value
) {
312 if (isString(key
) && isDictionary(value
)) {
313 CFStringRef circleName
= (CFStringRef
) key
;
315 CFDictionaryForEach(value
, ^(const void *key1
, const void *value1
) {
316 if (isString(key1
) && isDictionary(value1
)) {
317 peerID
= (CFStringRef
) key1
;
318 message
= CFRetainSafe((CFDictionaryRef
) value1
);
321 peerID
= (CFStringRef
) key1
;
322 message
= CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault
, key1
, value1
, NULL
);
324 SOSCircleForEachPeer(circle
, ^(SOSPeerInfoRef peer
) {
325 if(!CFEqualSafe(myPeer
, peer
)){
326 if(SOSPeerInfoShouldUseIDSMessageFragmentation(myPeer
, peer
)){
327 SOSTransportMessageIDSSetFragmentationPreference(transport
, kCFBooleanTrue
);
330 SOSTransportMessageIDSSetFragmentationPreference(transport
, kCFBooleanFalse
);
333 CFStringRef deviceID
= SOSPeerInfoCopyDeviceID(peer
);
334 if(CFStringCompare(SOSPeerInfoGetPeerID(peer
), peerID
, 0) == 0){
336 rx
= sendToPeer(transport
, circleName
, deviceID
, peerID
, message
, error
);
339 CFReleaseNull(deviceID
);
347 CFReleaseNull(message
);
352 static bool flushChanges(SOSTransportMessageRef transport
, CFErrorRef
*error
)
357 static bool cleanupAfterPeer(SOSTransportMessageRef transport
, CFDictionaryRef circle_to_peer_ids
, CFErrorRef
*error
)
362 void SOSTransportMessageIDSGetIDSDeviceID(SOSAccountRef account
){
364 CFStringRef deviceID
= SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account
->my_identity
));
365 if( deviceID
== NULL
|| CFStringGetLength(deviceID
) == 0){
366 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues
, CFErrorRef sync_error
){
367 bool success
= (sync_error
== NULL
);
369 secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", sync_error
);
372 secnotice("IDS Transport", "Successfully attempting to retrieve the IDS Device ID");
376 CFReleaseNull(deviceID
);