]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSTransportMessageIDS.m
Security-58286.51.6.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSTransportMessageIDS.m
1 //
2 // SOSTransportMessageIDS.c
3 // sec
4 //
5 //
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>
12
13 #include <SOSCloudCircleServer.h>
14 #include <Security/SecureObjectSync/SOSAccountPriv.h>
15 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
16
17 #include <utilities/SecCFWrappers.h>
18 #include <SOSInternal.h>
19 #include <AssertMacros.h>
20
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"
26
27 #define IDS "IDS transport"
28
29 @implementation SOSMessageIDS
30
31 @synthesize useFragmentation = useFragmentation;
32
33 -(CFIndex) SOSTransportMessageGetTransportType
34 {
35 return kIDS;
36 }
37
38 -(void) SOSTransportMessageIDSSetFragmentationPreference:(SOSMessageIDS*) transport pref:(CFBooleanRef) preference
39 {
40 useFragmentation = preference;
41 }
42
43 -(CFBooleanRef) SOSTransportMessageIDSGetFragmentationPreference:(SOSMessageIDS*) transport
44 {
45 return useFragmentation;
46 }
47
48 -(id) initWithAcount:(SOSAccount*)acct circleName:(CFStringRef)name
49 {
50 self = [super init];
51
52 if (self) {
53 self.useFragmentation = kCFBooleanTrue;
54 self.account = acct;
55 self.circleName = [[NSString alloc]initWithString:(__bridge NSString*)name];
56 [self SOSTransportMessageIDSGetIDSDeviceID:account];
57 SOSRegisterTransportMessage((SOSMessage*)self);
58 }
59
60 return self;
61 }
62
63 -(CFDictionaryRef) CF_RETURNS_RETAINED SOSTransportMessageHandlePeerMessageReturnsHandledCopy:(SOSMessage*) transport peerMessages:(CFMutableDictionaryRef) circle_peer_messages_table err:(CFErrorRef *)error
64 {
65 return CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
66 }
67
68 static HandleIDSMessageReason checkMessageValidity(SOSAccount* account, CFStringRef fromDeviceID, CFStringRef fromPeerID, CFStringRef *peerID, SOSPeerInfoRef *theirPeerInfo){
69 SOSAccountTrustClassic *trust = account.trust;
70 __block HandleIDSMessageReason reason = kHandleIDSMessageDontHandle;
71
72 SOSCircleForEachPeer(trust.trustedCircle, ^(SOSPeerInfoRef peer) {
73 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
74 CFStringRef pID = SOSPeerInfoGetPeerID(peer);
75
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;
82 return;
83 }
84 else if(CFStringCompare(fromDeviceID, deviceID, 0) != 0){ //IDSids do not match, ghost
85 secnotice("ids transport", "deviceIDMisMatch");
86 reason = kHandleIDSmessageDeviceIDMismatch;
87 CFReleaseNull(deviceID);
88 return;
89 }
90 else if(CFStringCompare(deviceID, fromDeviceID, 0) == 0){
91 *peerID = pID;
92 *theirPeerInfo = peer;
93 CFReleaseNull(deviceID);
94 reason = kHandleIDSMessageSuccess;
95 return;
96 }
97 else{
98 secerror("?? deviceID:%@, pID: %@, fromPeerID: %@, fromDeviceID: %@", deviceID, pID, fromPeerID, fromDeviceID);
99 }
100 }
101 }
102 CFReleaseNull(deviceID);
103 });
104
105 return reason;
106 }
107
108 -(HandleIDSMessageReason) SOSTransportMessageIDSHandleMessage:(SOSAccount*)acct m:(CFDictionaryRef) message err:(CFErrorRef *)error
109 {
110 secnotice("IDS Transport", "SOSTransportMessageIDSHandleMessage!");
111
112 CFStringRef dataKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeyIDSDataMessage, kCFStringEncodingASCII);
113 CFStringRef deviceIDKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeyDeviceID, kCFStringEncodingASCII);
114 CFStringRef sendersPeerIDKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeySendersPeerID, kCFStringEncodingASCII);
115 CFStringRef ourPeerIdKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeyPeerID, kCFStringEncodingASCII);
116 NSString *errMessage = nil;
117
118 HandleIDSMessageReason result = kHandleIDSMessageSuccess;
119
120 CFDataRef messageData = asData(CFDictionaryGetValue(message, dataKey), NULL);
121 __block CFStringRef fromDeviceID = asString(CFDictionaryGetValue(message, deviceIDKey), NULL);
122 __block CFStringRef fromPeerID = (CFStringRef)CFDictionaryGetValue(message, sendersPeerIDKey);
123 CFStringRef ourPeerID = asString(CFDictionaryGetValue(message, ourPeerIdKey), NULL);
124
125 CFStringRef peerID = NULL;
126 SOSPeerInfoRef theirPeer = NULL;
127
128 require_action_quiet(fromDeviceID, exit, result = kHandleIDSMessageDontHandle; errMessage = @"Missing device name");
129 require_action_quiet(fromPeerID, exit, result = kHandleIDSMessageDontHandle; errMessage = @"Missing from peer id");
130 require_action_quiet(messageData && CFDataGetLength(messageData) != 0, exit, result = kHandleIDSMessageDontHandle; errMessage = @"no message data");
131 require_action_quiet(SOSAccountHasFullPeerInfo(account, error), exit, result = kHandleIDSMessageNotReady; errMessage = @"no full perinfo");
132 require_action_quiet(ourPeerID && [account.peerID isEqual: (__bridge NSString*) ourPeerID], exit, result = kHandleIDSMessageDontHandle; secnotice("IDS Transport","ignoring message for: %@", ourPeerID));
133
134 require_quiet((result = checkMessageValidity( account, fromDeviceID, fromPeerID, &peerID, &theirPeer)) == kHandleIDSMessageSuccess, exit);
135
136 if ([account.ids_message_transport SOSTransportMessageHandlePeerMessage:account.ids_message_transport id:peerID cm:messageData err:error]) {
137 CFMutableDictionaryRef peersToSyncWith = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
138 CFMutableSetRef peerIDs = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
139 CFSetAddValue(peerIDs, peerID);
140 SOSAccountTrustClassic* trust = account.trust;
141 //sync using fragmentation?
142 if(SOSPeerInfoShouldUseIDSMessageFragmentation(trust.peerInfo, theirPeer)){
143 //set useFragmentation bit
144 [account.ids_message_transport SOSTransportMessageIDSSetFragmentationPreference:account.ids_message_transport pref: kCFBooleanTrue];
145 }
146 else{
147 [account.ids_message_transport SOSTransportMessageIDSSetFragmentationPreference:account.ids_message_transport pref: kCFBooleanFalse];
148 }
149
150 if(![account.ids_message_transport SOSTransportMessageSyncWithPeers:account.ids_message_transport p:peerIDs err:error]){
151 secerror("SOSTransportMessageIDSHandleMessage Could not sync with all peers: %@", *error);
152 }else{
153 secnotice("IDS Transport", "Synced with all peers!");
154 }
155
156 CFReleaseNull(peersToSyncWith);
157 CFReleaseNull(peerIDs);
158 }else{
159 if(error && *error != NULL){
160 CFStringRef errorMessage = CFErrorCopyDescription(*error);
161 if (-25308 == CFErrorGetCode(*error)) { // tell KeychainSyncingOverIDSProxy to call us back when device unlocks
162 result = kHandleIDSMessageLocked;
163 }else{ //else drop it, couldn't handle the message
164 result = kHandleIDSMessageDontHandle;
165 }
166 secerror("IDS Transport Could not handle message: %@, %@", messageData, *error);
167 CFReleaseNull(errorMessage);
168
169 }
170 else{ //no error but failed? drop it, log message
171 secerror("IDS Transport Could not handle message: %@", messageData);
172 result = kHandleIDSMessageDontHandle;
173
174 }
175 }
176
177 exit:
178
179 if(errMessage != nil){
180 secerror("%@", errMessage);
181 }
182 CFReleaseNull(ourPeerIdKey);
183 CFReleaseNull(sendersPeerIDKey);
184 CFReleaseNull(deviceIDKey);
185 CFReleaseNull(dataKey);
186 return result;
187 }
188
189
190 static bool sendToPeer(SOSMessageIDS* transport, bool shouldUseAckModel, CFStringRef circleName, CFStringRef deviceID, CFStringRef peerID,CFDictionaryRef message, CFErrorRef *error)
191 {
192 __block bool success = false;
193 CFStringRef errorMessage = NULL;
194 CFDictionaryRef userInfo;
195 CFStringRef operation = NULL;
196 CFDataRef operationData = NULL;
197 CFMutableDataRef mutableData = NULL;
198 SOSAccount* account = [transport SOSTransportMessageGetAccount];
199 CFStringRef ourPeerID = SOSPeerInfoGetPeerID(account.peerInfo);
200 CFStringRef operationToString = NULL;
201
202 CFDictionaryRef messagetoSend = NULL;
203
204 if(deviceID == NULL || CFStringGetLength(deviceID) == 0){
205 errorMessage = CFSTR("Need an IDS Device ID to sync");
206 userInfo = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, kCFErrorLocalizedDescriptionKey, errorMessage, NULL);
207 if(error != NULL){
208 *error =CFErrorCreate(kCFAllocatorDefault, CFSTR("com.apple.security.ids.error"), kSecIDSErrorNoDeviceID, userInfo);
209 secerror("%@", *error);
210 }
211 CFReleaseNull(messagetoSend);
212 CFReleaseNull(operation);
213 CFReleaseNull(operationData);
214 CFReleaseNull(mutableData);
215 CFReleaseNull(userInfo);
216 CFReleaseNull(operationToString);
217
218 return success;
219 }
220
221 if(CFDictionaryGetValue(message, kIDSOperationType) == NULL && [transport SOSTransportMessageIDSGetFragmentationPreference:transport] == kCFBooleanTrue){
222 //handle a keychain data blob using fragmentation!
223 secnotice("IDS Transport","sendToPeer: using fragmentation!");
224
225 operationToString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), kIDSKeychainSyncIDSFragmentation);
226 messagetoSend = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
227 kIDSOperationType, operationToString,
228 kIDSMessageRecipientDeviceID, deviceID,
229 kIDSMessageRecipientPeerID, peerID,
230 kIDSMessageUsesAckModel, (shouldUseAckModel ? CFSTR("YES") : CFSTR("NO")),
231 kIDSMessageToSendKey, message,
232 NULL);
233 }
234 else{ //otherhandle handle the test message without fragmentation
235 secnotice("IDS Transport","sendToPeer: not going to fragment message");
236
237 CFMutableDictionaryRef annotatedMessage = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, message);
238 CFDictionaryAddValue(annotatedMessage, kIDSMessageRecipientPeerID, peerID);
239 CFDictionaryAddValue(annotatedMessage, kIDSMessageRecipientDeviceID, deviceID);
240 CFDictionaryAddValue(annotatedMessage, kIDSMessageUsesAckModel, (shouldUseAckModel ? CFSTR("YES") : CFSTR("NO")));
241 CFTransferRetained(messagetoSend, annotatedMessage);
242 CFReleaseNull(annotatedMessage);
243 }
244
245 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
246
247 secnotice("IDS Transport", "Starting");
248
249 SecADAddValueForScalarKey(CFSTR("com.apple.security.sos.sendids"), 1);
250
251 CFStringRef myDeviceID = CFRetainSafe((__bridge CFStringRef)account.deviceID);
252 if(!myDeviceID){
253 myDeviceID = SOSPeerInfoCopyDeviceID(account.peerInfo);
254 }
255
256 SOSCloudKeychainSendIDSMessage(messagetoSend, deviceID, ourPeerID, myDeviceID, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), [transport SOSTransportMessageIDSGetFragmentationPreference:transport], ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
257 success = (sync_error == NULL);
258 if (sync_error && error) {
259 CFRetainAssign(*error, sync_error);
260 }
261
262 dispatch_semaphore_signal(wait_for);
263 });
264
265 if (dispatch_semaphore_wait(wait_for, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 2)) != 0) {
266 secerror("IDS Transport: timed out waiting for message send to complete");
267 }
268
269 if(!success){
270 if(error != NULL)
271 secerror("IDS Transport: Failed to send message to peer! %@", *error);
272 else
273 secerror("IDS Transport: Failed to send message to peer");
274 }
275 else{
276 secnotice("IDS Transport", "Sent message to peer!");
277 }
278 CFReleaseNull(myDeviceID);
279 CFReleaseNull(messagetoSend);
280 CFReleaseNull(operation);
281 CFReleaseNull(operationData);
282 CFReleaseNull(mutableData);
283 CFReleaseNull(operationToString);
284 return success;
285 }
286
287
288 -(bool) SOSTransportMessageSyncWithPeers:(SOSMessageIDS*) transport p:(CFSetRef) peers err:(CFErrorRef *)error
289 {
290 // Each entry is keyed by circle name and contains a list of peerIDs
291 __block bool result = true;
292
293 CFSetForEach(peers, ^(const void *value) {
294 CFStringRef peerID = asString(value, NULL);
295
296 result &= [transport SOSTransportMessageSendMessageIfNeeded:transport id:(__bridge CFStringRef)(transport.circleName) pID:peerID err:error];
297 });
298
299 return result;
300 }
301
302 -(bool) SOSTransportMessageSendMessages:(SOSMessageIDS*) transport pm:(CFDictionaryRef) peer_messages err:(CFErrorRef *)error
303 {
304 __block bool result = true;
305
306 SOSPeerInfoRef myPeer = transport->account.peerInfo;
307 CFStringRef myID = SOSPeerInfoGetPeerID(myPeer);
308 if(!myPeer)
309 return result;
310
311 CFDictionaryForEach(peer_messages, ^(const void *key, const void *value) {
312 CFErrorRef error = NULL;
313
314 SOSPeerInfoRef peer = NULL;
315 CFStringRef deviceID = NULL;
316 CFDictionaryRef message = NULL;
317
318 CFStringRef peerID = asString(key, &error);
319 require_quiet(peerID, skip);
320 require_quiet(!CFEqualSafe(myID, key), skip);
321
322 message = CFRetainSafe(asDictionary(value, &error));
323 if (message == NULL) {
324 // If it's not a data, return the error
325 CFDataRef messageData = asData(value, NULL);
326 if (messageData) {
327 CFReleaseNull(error);
328 message = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, peerID, messageData, NULL);
329 }
330 }
331 require_quiet(message, skip);
332
333 peer = SOSAccountCopyPeerWithID(transport->account, peerID, &error);
334 require_quiet(peer, skip);
335
336 deviceID = SOSPeerInfoCopyDeviceID(peer);
337 require_action_quiet(deviceID, skip, SOSErrorCreate(kSOSErrorSendFailure, &error, NULL, CFSTR("No IDS ID")));
338
339 [transport SOSTransportMessageIDSSetFragmentationPreference:transport
340 pref:SOSPeerInfoShouldUseIDSMessageFragmentation(myPeer, peer) ? kCFBooleanTrue : kCFBooleanFalse];
341 bool shouldUseAckModel = SOSPeerInfoShouldUseACKModel(myPeer, peer);
342
343 result &= sendToPeer(transport, shouldUseAckModel, (__bridge CFStringRef)(transport.circleName), deviceID, peerID, message, &error);
344
345 skip:
346 if (error) {
347 secerror("Failed to sync to %@ over IDS: %@", peerID, error);
348 }
349
350 CFReleaseNull(peer);
351 CFReleaseNull(deviceID);
352 CFReleaseNull(message);
353
354 CFReleaseNull(error);
355 });
356
357 return result;
358 }
359
360
361 -(bool) SOSTransportMessageFlushChanges:(SOSMessageIDS*) transport err:(CFErrorRef *)error
362 {
363 return true;
364 }
365
366 -(bool) SOSTransportMessageCleanupAfterPeerMessages:(SOSMessageIDS*) transport peers:(CFDictionaryRef) peers err:(CFErrorRef*) error
367 {
368 return true;
369 }
370
371 -(bool) SOSTransportMessageIDSGetIDSDeviceID:(SOSAccount*)acct
372 {
373 SOSAccountTrustClassic* trust = acct.trust;
374 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(trust.peerInfo);
375 bool hasDeviceID = (deviceID != NULL && CFStringGetLength(deviceID) != 0) || account.deviceID;
376 CFReleaseNull(deviceID);
377
378 if(!hasDeviceID){
379 SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
380 bool success = (sync_error == NULL);
381 if (!success) {
382 secerror("Could not ask KeychainSyncingOverIDSProxy for Device ID: %@", sync_error);
383 }
384 else{
385 secnotice("IDS Transport", "Successfully attempting to retrieve the IDS Device ID");
386 }
387 });
388 }
389
390 return hasDeviceID;
391 }
392 @end