2 // SOSTransportMessageIDS.c
6 #include <Security/SecBasePriv.h>
7 #include <Security/SecureObjectSync/SOSTransport.h>
8 #import <Security/SecureObjectSync/SOSTransportMessage.h>
9 #import <Security/SecureObjectSync/SOSAccountPriv.h>
10 #include <Security/SecureObjectSync/SOSKVSKeys.h>
11 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
13 #include <SOSCloudCircleServer.h>
14 #include <Security/SecureObjectSync/SOSAccountPriv.h>
15 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
17 #include <utilities/SecCFWrappers.h>
18 #include <SOSInternal.h>
19 #include <AssertMacros.h>
21 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
22 #include <SOSCircle/CKBridge/SOSCloudKeychainConstants.h>
23 #include <Security/SecureObjectSync/SOSInternal.h>
24 #include <utilities/SecADWrapper.h>
25 #import "Security/SecureObjectSync/SOSAccountTrustClassic.h"
27 #define IDS "IDS transport"
29 @implementation SOSMessageIDS
31 @synthesize useFragmentation = useFragmentation;
33 -(CFIndex) SOSTransportMessageGetTransportType
38 -(void) SOSTransportMessageIDSSetFragmentationPreference:(SOSMessageIDS*) transport pref:(CFBooleanRef) preference
40 useFragmentation = preference;
43 -(CFBooleanRef) SOSTransportMessageIDSGetFragmentationPreference:(SOSMessageIDS*) transport
45 return useFragmentation;
48 -(id) initWithAcount:(SOSAccount*)acct circleName:(CFStringRef)name
53 self.useFragmentation = kCFBooleanTrue;
55 self.circleName = [[NSString alloc]initWithString:(__bridge NSString*)name];
56 [self SOSTransportMessageIDSGetIDSDeviceID:account];
57 SOSRegisterTransportMessage((SOSMessage*)self);
63 -(CFDictionaryRef) CF_RETURNS_RETAINED SOSTransportMessageHandlePeerMessageReturnsHandledCopy:(SOSMessage*) transport peerMessages:(CFMutableDictionaryRef) circle_peer_messages_table err:(CFErrorRef *)error
65 return CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
68 static HandleIDSMessageReason checkMessageValidity(SOSAccount* account, CFStringRef fromDeviceID, CFStringRef fromPeerID, CFStringRef *peerID, SOSPeerInfoRef *theirPeerInfo){
69 SOSAccountTrustClassic *trust = account.trust;
70 __block HandleIDSMessageReason reason = kHandleIDSMessageDontHandle;
72 SOSCircleForEachPeer(trust.trustedCircle, ^(SOSPeerInfoRef peer) {
73 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
74 CFStringRef pID = SOSPeerInfoGetPeerID(peer);
76 if( deviceID && pID && fromPeerID && fromDeviceID && CFStringGetLength(fromPeerID) != 0 ){
77 if(CFStringCompare(pID, fromPeerID, 0) == 0){
78 if(CFStringGetLength(deviceID) == 0){
79 secnotice("ids transport", "device ID was empty in the peer list, holding on to message");
80 CFReleaseNull(deviceID);
81 reason = kHandleIDSMessageNotReady;
84 else if(CFStringCompare(fromDeviceID, deviceID, 0) != 0){ //IDSids do not match, ghost
85 reason = kHandleIDSmessageDeviceIDMismatch;
86 CFReleaseNull(deviceID);
89 else if(CFStringCompare(deviceID, fromDeviceID, 0) == 0){
91 *theirPeerInfo = peer;
92 CFReleaseNull(deviceID);
93 reason = kHandleIDSMessageSuccess;
98 CFReleaseNull(deviceID);
104 -(HandleIDSMessageReason) SOSTransportMessageIDSHandleMessage:(SOSAccount*)acct m:(CFDictionaryRef) message err:(CFErrorRef *)error
106 secnotice("IDS Transport", "SOSTransportMessageIDSHandleMessage!");
108 CFStringRef dataKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeyIDSDataMessage, kCFStringEncodingASCII);
109 CFStringRef deviceIDKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeyDeviceID, kCFStringEncodingASCII);
110 CFStringRef sendersPeerIDKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeySendersPeerID, kCFStringEncodingASCII);
111 CFStringRef ourPeerIdKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeyPeerID, kCFStringEncodingASCII);
113 HandleIDSMessageReason result = kHandleIDSMessageSuccess;
115 CFDataRef messageData = asData(CFDictionaryGetValue(message, dataKey), NULL);
116 __block CFStringRef fromDeviceID = asString(CFDictionaryGetValue(message, deviceIDKey), NULL);
117 __block CFStringRef fromPeerID = (CFStringRef)CFDictionaryGetValue(message, sendersPeerIDKey);
118 CFStringRef ourPeerID = asString(CFDictionaryGetValue(message, ourPeerIdKey), NULL);
120 CFStringRef peerID = NULL;
121 SOSPeerInfoRef theirPeer = NULL;
123 require_action_quiet(fromDeviceID, exit, result = kHandleIDSMessageDontHandle);
124 require_action_quiet(fromPeerID, exit, result = kHandleIDSMessageDontHandle);
125 require_action_quiet(messageData && CFDataGetLength(messageData) != 0, exit, result = kHandleIDSMessageDontHandle);
126 require_action_quiet(SOSAccountHasFullPeerInfo(account, error), exit, result = kHandleIDSMessageNotReady);
127 require_action_quiet(ourPeerID && [account.peerID isEqual: (__bridge NSString*) ourPeerID], exit, result = kHandleIDSMessageDontHandle; secnotice("IDS Transport","ignoring message for: %@", ourPeerID));
129 require_quiet((result = checkMessageValidity( account, fromDeviceID, fromPeerID, &peerID, &theirPeer)) == kHandleIDSMessageSuccess, exit);
131 if ([account.ids_message_transport SOSTransportMessageHandlePeerMessage:account.ids_message_transport id:peerID cm:messageData err:error]) {
132 CFMutableDictionaryRef peersToSyncWith = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
133 CFMutableSetRef peerIDs = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
134 CFSetAddValue(peerIDs, peerID);
135 SOSAccountTrustClassic* trust = account.trust;
136 //sync using fragmentation?
137 if(SOSPeerInfoShouldUseIDSMessageFragmentation(trust.peerInfo, theirPeer)){
138 //set useFragmentation bit
139 [account.ids_message_transport SOSTransportMessageIDSSetFragmentationPreference:account.ids_message_transport pref: kCFBooleanTrue];
142 [account.ids_message_transport SOSTransportMessageIDSSetFragmentationPreference:account.ids_message_transport pref: kCFBooleanFalse];
145 if(![account.ids_message_transport SOSTransportMessageSyncWithPeers:account.ids_message_transport p:peerIDs err:error]){
146 secerror("SOSTransportMessageIDSHandleMessage Could not sync with all peers: %@", *error);
148 secnotice("IDS Transport", "Synced with all peers!");
151 CFReleaseNull(peersToSyncWith);
152 CFReleaseNull(peerIDs);
154 if(error && *error != NULL){
155 CFStringRef errorMessage = CFErrorCopyDescription(*error);
156 if (-25308 == CFErrorGetCode(*error)) { // tell KeychainSyncingOverIDSProxy to call us back when device unlocks
157 result = kHandleIDSMessageLocked;
158 }else{ //else drop it, couldn't handle the message
159 result = kHandleIDSMessageDontHandle;
161 secerror("IDS Transport Could not handle message: %@, %@", messageData, *error);
162 CFReleaseNull(errorMessage);
165 else{ //no error but failed? drop it, log message
166 secerror("IDS Transport Could not handle message: %@", messageData);
167 result = kHandleIDSMessageDontHandle;
173 CFReleaseNull(ourPeerIdKey);
174 CFReleaseNull(sendersPeerIDKey);
175 CFReleaseNull(deviceIDKey);
176 CFReleaseNull(dataKey);
181 static bool sendToPeer(SOSMessageIDS* transport, bool shouldUseAckModel, CFStringRef circleName, CFStringRef deviceID, CFStringRef peerID,CFDictionaryRef message, CFErrorRef *error)
183 __block bool success = false;
184 CFStringRef errorMessage = NULL;
185 CFDictionaryRef userInfo;
186 CFStringRef operation = NULL;
187 CFDataRef operationData = NULL;
188 CFMutableDataRef mutableData = NULL;
189 SOSAccount* account = [transport SOSTransportMessageGetAccount];
190 CFStringRef ourPeerID = SOSPeerInfoGetPeerID(account.peerInfo);
191 CFStringRef operationToString = NULL;
193 CFDictionaryRef messagetoSend = NULL;
195 if(deviceID == NULL || CFStringGetLength(deviceID) == 0){
196 errorMessage = CFSTR("Need an IDS Device ID to sync");
197 userInfo = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, kCFErrorLocalizedDescriptionKey, errorMessage, NULL);
199 *error =CFErrorCreate(kCFAllocatorDefault, CFSTR("com.apple.security.ids.error"), kSecIDSErrorNoDeviceID, userInfo);
200 secerror("%@", *error);
202 CFReleaseNull(messagetoSend);
203 CFReleaseNull(operation);
204 CFReleaseNull(operationData);
205 CFReleaseNull(mutableData);
206 CFReleaseNull(userInfo);
207 CFReleaseNull(operationToString);
212 if(CFDictionaryGetValue(message, kIDSOperationType) == NULL && [transport SOSTransportMessageIDSGetFragmentationPreference:transport] == kCFBooleanTrue){
213 //handle a keychain data blob using fragmentation!
214 secnotice("IDS Transport","sendToPeer: using fragmentation!");
216 operationToString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSKeychainSyncIDSFragmentation);
217 messagetoSend = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
218 kIDSOperationType, operationToString,
219 kIDSMessageRecipientDeviceID, deviceID,
220 kIDSMessageRecipientPeerID, peerID,
221 kIDSMessageUsesAckModel, (shouldUseAckModel ? CFSTR("YES") : CFSTR("NO")),
222 kIDSMessageToSendKey, message,
225 else{ //otherhandle handle the test message without fragmentation
226 secnotice("IDS Transport","sendToPeer: not going to fragment message");
228 CFMutableDictionaryRef annotatedMessage = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, message);
229 CFDictionaryAddValue(annotatedMessage, kIDSMessageRecipientPeerID, peerID);
230 CFDictionaryAddValue(annotatedMessage, kIDSMessageRecipientDeviceID, deviceID);
231 CFDictionaryAddValue(annotatedMessage, kIDSMessageUsesAckModel, (shouldUseAckModel ? CFSTR("YES") : CFSTR("NO")));
232 CFTransferRetained(messagetoSend, annotatedMessage);
233 CFReleaseNull(annotatedMessage);
236 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
238 secnotice("IDS Transport", "Starting");
239 SecADAddValueForScalarKey(CFSTR("com.apple.security.sos.sendids"), 1);
241 SOSCloudKeychainSendIDSMessage(messagetoSend, deviceID, ourPeerID, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), [transport SOSTransportMessageIDSGetFragmentationPreference:transport], ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
242 success = (sync_error == NULL);
243 if (sync_error && error) {
244 CFRetainAssign(*error, sync_error);
247 dispatch_semaphore_signal(wait_for);
250 if (dispatch_semaphore_wait(wait_for, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 2)) != 0) {
251 secerror("IDS Transport: timed out waiting for message send to complete");
256 secerror("IDS Transport: Failed to send message to peer! %@", *error);
258 secerror("IDS Transport: Failed to send message to peer");
261 secnotice("IDS Transport", "Sent message to peer!");
264 CFReleaseNull(messagetoSend);
265 CFReleaseNull(operation);
266 CFReleaseNull(operationData);
267 CFReleaseNull(mutableData);
268 CFReleaseNull(operationToString);
273 -(bool) SOSTransportMessageSyncWithPeers:(SOSMessageIDS*) transport p:(CFSetRef) peers err:(CFErrorRef *)error
275 // Each entry is keyed by circle name and contains a list of peerIDs
276 __block bool result = true;
278 CFSetForEach(peers, ^(const void *value) {
279 CFStringRef peerID = asString(value, NULL);
281 result &= [transport SOSTransportMessageSendMessageIfNeeded:transport id:(__bridge CFStringRef)(transport.circleName) pID:peerID err:error];
287 -(bool) SOSTransportMessageSendMessages:(SOSMessageIDS*) transport pm:(CFDictionaryRef) peer_messages err:(CFErrorRef *)error
289 __block bool result = true;
291 SOSPeerInfoRef myPeer = transport->account.peerInfo;
292 CFStringRef myID = SOSPeerInfoGetPeerID(myPeer);
296 CFDictionaryForEach(peer_messages, ^(const void *key, const void *value) {
297 CFErrorRef error = NULL;
299 SOSPeerInfoRef peer = NULL;
300 CFStringRef deviceID = NULL;
301 CFDictionaryRef message = NULL;
303 CFStringRef peerID = asString(key, &error);
304 require_quiet(peerID, skip);
305 require_quiet(!CFEqualSafe(myID, key), skip);
307 message = CFRetainSafe(asDictionary(value, &error));
308 if (message == NULL) {
309 // If it's not a data, return the error
310 CFDataRef messageData = asData(value, NULL);
312 CFReleaseNull(error);
313 message = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, peerID, messageData, NULL);
316 require_quiet(message, skip);
318 peer = SOSAccountCopyPeerWithID(transport->account, peerID, &error);
319 require_quiet(peer, skip);
321 deviceID = SOSPeerInfoCopyDeviceID(peer);
322 require_action_quiet(deviceID, skip, SOSErrorCreate(kSOSErrorSendFailure, &error, NULL, CFSTR("No IDS ID")));
324 [transport SOSTransportMessageIDSSetFragmentationPreference:transport
325 pref:SOSPeerInfoShouldUseIDSMessageFragmentation(myPeer, peer) ? kCFBooleanTrue : kCFBooleanFalse];
326 bool shouldUseAckModel = SOSPeerInfoShouldUseACKModel(myPeer, peer);
328 result &= sendToPeer(transport, shouldUseAckModel, (__bridge CFStringRef)(transport.circleName), deviceID, peerID, message, &error);
332 secerror("Failed to sync to %@ over IDS: %@", peerID, error);
336 CFReleaseNull(deviceID);
337 CFReleaseNull(message);
339 CFReleaseNull(error);
346 -(bool) SOSTransportMessageFlushChanges:(SOSMessageIDS*) transport err:(CFErrorRef *)error
351 -(bool) SOSTransportMessageCleanupAfterPeerMessages:(SOSMessageIDS*) transport peers:(CFDictionaryRef) peers err:(CFErrorRef*) error
356 -(bool) SOSTransportMessageIDSGetIDSDeviceID:(SOSAccount*)acct
358 SOSAccountTrustClassic* trust = acct.trust;
359 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(trust.peerInfo);
360 bool hasDeviceID = deviceID != NULL && CFStringGetLength(deviceID) != 0;
361 CFReleaseNull(deviceID);
364 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
365 bool success = (sync_error == NULL);
367 secerror("Could not ask KeychainSyncingOverIDSProxy for Device ID: %@", sync_error);
370 secnotice("IDS Transport", "Successfully attempting to retrieve the IDS Device ID");