]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSTransportMessageIDS.c
2841b08f15724e694afd68b71839668e4177cd9e
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSTransportMessageIDS.c
1 //
2 // SOSTransportMessageIDS.c
3 // sec
4 //
5 //
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>
11
12 #include <SOSCloudCircleServer.h>
13 #include <Security/SecureObjectSync/SOSAccountPriv.h>
14 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
15
16 #include <utilities/SecCFWrappers.h>
17 #include <SOSInternal.h>
18 #include <AssertMacros.h>
19
20 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
21 #include <SOSCircle/CKBridge/SOSCloudKeychainConstants.h>
22 #include <Security/SecureObjectSync/SOSInternal.h>
23
24 #define IDS "IDS transport"
25
26 struct __OpaqueSOSTransportMessageIDS {
27 struct __OpaqueSOSTransportMessage m;
28 CFBooleanRef useFragmentation;
29 };
30
31 //
32 // V-table implementation forward declarations
33 //
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);
41
42 static inline CFIndex getTransportType(SOSTransportMessageRef transport, CFErrorRef *error){
43 return kIDS;
44 }
45
46 void SOSTransportMessageIDSSetFragmentationPreference(SOSTransportMessageRef transport, CFBooleanRef preference){
47 SOSTransportMessageIDSRef t = (SOSTransportMessageIDSRef)transport;
48 t->useFragmentation = preference;
49 }
50
51 CFBooleanRef SOSTransportMessageIDSGetFragmentationPreference(SOSTransportMessageRef transport){
52 SOSTransportMessageIDSRef t = (SOSTransportMessageIDSRef)transport;
53 return t->useFragmentation;
54 }
55
56 SOSTransportMessageIDSRef SOSTransportMessageIDSCreate(SOSAccountRef account, CFStringRef circleName, CFErrorRef *error)
57 {
58 SOSTransportMessageIDSRef ids = (SOSTransportMessageIDSRef) SOSTransportMessageCreateForSubclass(sizeof(struct __OpaqueSOSTransportMessageIDS) - sizeof(CFRuntimeBase), account, circleName, error);
59
60 if (ids) {
61 // Fill in vtable:
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;
69
70 // Initialize ourselves
71
72 SOSTransportMessageIDSGetIDSDeviceID(account);
73 SOSRegisterTransportMessage((SOSTransportMessageRef)ids);
74 }
75
76 return ids;
77 }
78 static void destroy(SOSTransportMessageRef transport){
79 SOSUnregisterTransportMessage(transport);
80 }
81
82 static CF_RETURNS_RETAINED CFDictionaryRef handleMessages(SOSTransportMessageRef transport, CFMutableDictionaryRef circle_peer_messages_table, CFErrorRef *error) {
83 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault, NULL);
84 }
85
86 static HandleIDSMessageReason checkMessageValidity(SOSAccountRef account, CFStringRef fromDeviceID, CFStringRef fromPeerID, CFStringRef *peerID, SOSPeerInfoRef *theirPeerInfo){
87
88 __block HandleIDSMessageReason reason = kHandleIDSMessageDontHandle;
89
90 SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
91 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
92 CFStringRef pID = SOSPeerInfoGetPeerID(peer);
93
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;
100 return;
101 }
102 else if(CFStringCompare(fromDeviceID, deviceID, 0) != 0){ //IDSids do not match, ghost
103 reason = kHandleIDSmessageDeviceIDMismatch;
104 CFReleaseNull(deviceID);
105 return;
106 }
107 else if(CFStringCompare(deviceID, fromDeviceID, 0) == 0){
108 *peerID = pID;
109 *theirPeerInfo = peer;
110 CFReleaseNull(deviceID);
111 reason = kHandleIDSMessageSuccess;
112 return;
113 }
114 }
115 }
116 CFReleaseNull(deviceID);
117 });
118
119 return reason;
120 }
121
122 HandleIDSMessageReason SOSTransportMessageIDSHandleMessage(SOSAccountRef account, CFDictionaryRef message, CFErrorRef *error) {
123
124 secnotice("IDS Transport", "SOSTransportMessageIDSHandleMessage!");
125
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);
130
131 HandleIDSMessageReason result = kHandleIDSMessageSuccess;
132
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);
137
138 CFStringRef peerID = NULL;
139 SOSPeerInfoRef theirPeer = NULL;
140
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));
146
147 require_quiet((result = checkMessageValidity( account, fromDeviceID, fromPeerID, &peerID, &theirPeer)) == kHandleIDSMessageSuccess, exit);
148
149 if (SOSTransportMessageHandlePeerMessage(account->ids_message_transport, peerID, messageData, error)) {
150 CFMutableDictionaryRef peersToSyncWith = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
151 CFMutableSetRef peerIDs = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
152 CFSetAddValue(peerIDs, peerID);
153
154 //sync using fragmentation?
155 if(SOSPeerInfoShouldUseIDSMessageFragmentation(SOSFullPeerInfoGetPeerInfo(account->my_identity), theirPeer)){
156 //set useFragmentation bit
157 SOSTransportMessageIDSSetFragmentationPreference(account->ids_message_transport, kCFBooleanTrue);
158 }
159 else{
160 SOSTransportMessageIDSSetFragmentationPreference(account->ids_message_transport, kCFBooleanFalse);
161 }
162
163 if(!SOSTransportMessageSyncWithPeers(account->ids_message_transport, peerIDs, error)){
164 secerror("SOSTransportMessageIDSHandleMessage Could not sync with all peers: %@", *error);
165 }else{
166 secnotice("IDS Transport", "Synced with all peers!");
167 }
168
169 CFReleaseNull(peersToSyncWith);
170 CFReleaseNull(peerIDs);
171 }else{
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;
178 }
179 secerror("IDS Transport Could not handle message: %@, %@", messageData, *error);
180 CFReleaseNull(errorMessage);
181
182 }
183 else{ //no error but failed? drop it, log message
184 secerror("IDS Transport Could not handle message: %@", messageData);
185 result = kHandleIDSMessageDontHandle;
186
187 }
188 }
189
190 exit:
191 CFReleaseNull(ourPeerIdKey);
192 CFReleaseNull(sendersPeerIDKey);
193 CFReleaseNull(deviceIDKey);
194 CFReleaseNull(dataKey);
195 return result;
196 }
197
198
199 static bool sendToPeer(SOSTransportMessageRef transport, bool shouldUseAckModel, CFStringRef circleName, CFStringRef deviceID, CFStringRef peerID,CFDictionaryRef message, CFErrorRef *error)
200 {
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;
210
211 CFDictionaryRef messagetoSend = NULL;
212
213 require_action_quiet((deviceID != NULL && CFStringGetLength(deviceID) >0), fail, errorMessage = CFSTR("Need an IDS Device ID to sync"));
214
215 if(CFDictionaryGetValue(message, kIDSOperationType) == NULL && SOSTransportMessageIDSGetFragmentationPreference(transport) == kCFBooleanTrue){
216 //handle a keychain data blob using fragmentation!
217 secnotice("IDS Transport","sendToPeer: using fragmentation!");
218
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,
226 NULL);
227 }
228 else{ //otherhandle handle the test message without fragmentation
229 secnotice("IDS Transport","sendToPeer: not going to fragment message");
230
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);
237 }
238
239 dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
240 dispatch_retain(wait_for); // Both this scope and the block own it.
241
242 secnotice("ids transport", "Starting");
243
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);
248 }
249
250 dispatch_semaphore_signal(wait_for);
251 dispatch_release(wait_for);
252 });
253
254 dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
255 dispatch_release(wait_for);
256
257 if(!success){
258 if(error != NULL)
259 secerror("Failed to send message to peer! %@", *error);
260 else
261 secerror("Failed to send message to peer");
262 }
263 else{
264 secnotice("IDS Transport", "Sent message to peer!");
265 }
266
267 CFReleaseNull(messagetoSend);
268 CFReleaseNull(operation);
269 CFReleaseNull(operationData);
270 CFReleaseNull(mutableData);
271 CFReleaseNull(operationToString);
272 return success;
273
274 fail:
275 userInfo = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, kCFErrorLocalizedDescriptionKey, errorMessage, NULL);
276 if(error != NULL){
277 *error =CFErrorCreate(kCFAllocatorDefault, CFSTR("com.apple.security.ids.error"), kSecIDSErrorNoDeviceID, userInfo);
278 secerror("%@", *error);
279 }
280 CFReleaseNull(messagetoSend);
281 CFReleaseNull(operation);
282 CFReleaseNull(operationData);
283 CFReleaseNull(mutableData);
284 CFReleaseNull(userInfo);
285 CFReleaseNull(operationToString);
286
287 return success;
288 }
289
290
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;
294
295 CFSetForEach(peers, ^(const void *value) {
296 CFStringRef peerID = asString(value, NULL);
297 result &= SOSTransportMessageSendMessageIfNeeded(transport, transport->circleName, peerID, error);
298 });
299
300 return result;
301 }
302
303 static bool sendMessages(SOSTransportMessageRef transport, CFDictionaryRef peersToMessage, CFErrorRef *error) {
304 __block bool result = true;
305
306 SOSPeerInfoRef myPeer = SOSAccountGetMyPeerInfo(transport->account);
307 CFStringRef myID = SOSPeerInfoGetPeerID(myPeer);
308 require_quiet(myPeer, fail);
309
310 CFStringRef circleName = transport->circleName;
311
312 CFDictionaryForEach(peersToMessage, ^(const void *key, const void *value) {
313 CFErrorRef error = NULL;
314
315 SOSPeerInfoRef peer = NULL;
316 CFStringRef deviceID = NULL;
317 CFDictionaryRef message = NULL;
318
319 CFStringRef peerID = asString(key, &error);
320 require_quiet(peerID, skip);
321 require_quiet(!CFEqualSafe(myID, key), skip);
322
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);
327 if (messageData) {
328 CFReleaseNull(error);
329 message = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, peerID, messageData, NULL);
330 }
331 }
332 require_quiet(message, skip);
333
334 peer = SOSAccountCopyPeerWithID(transport->account, peerID, &error);
335 require_quiet(peer, skip);
336
337 deviceID = SOSPeerInfoCopyDeviceID(peer);
338 require_action_quiet(deviceID, skip, SOSErrorCreate(kSOSErrorSendFailure, &error, NULL, CFSTR("No IDS ID")));
339
340 SOSTransportMessageIDSSetFragmentationPreference(transport,
341 SOSPeerInfoShouldUseIDSMessageFragmentation(myPeer, peer) ? kCFBooleanTrue : kCFBooleanFalse);
342 bool shouldUseAckModel = SOSPeerInfoShouldUseACKModel(myPeer, peer);
343
344 result &= sendToPeer(transport, shouldUseAckModel, circleName, deviceID, peerID, message, &error);
345
346 skip:
347 if (error) {
348 secerror("Failed to sync to %@ over IDS: %@", peerID, error);
349 }
350
351 CFReleaseNull(peer);
352 CFReleaseNull(deviceID);
353 CFReleaseNull(message);
354
355 CFReleaseNull(error);
356 });
357
358 fail:
359 return result;
360 }
361
362
363 static bool flushChanges(SOSTransportMessageRef transport, CFErrorRef *error)
364 {
365 return true;
366 }
367
368 static bool cleanupAfterPeer(SOSTransportMessageRef transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error)
369 {
370 return true;
371 }
372
373 bool SOSTransportMessageIDSGetIDSDeviceID(SOSAccountRef account){
374 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
375 bool hasDeviceID = deviceID != NULL && CFStringGetLength(deviceID) != 0;
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 }