]>
Commit | Line | Data |
---|---|---|
5c19dc3a A |
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 | ||
23 | ||
24 | #define IDS "IDS transport" | |
25 | ||
26 | struct __OpaqueSOSTransportMessageIDS { | |
27 | struct __OpaqueSOSTransportMessage m; | |
fa7225c8 | 28 | CFBooleanRef useFragmentation; |
5c19dc3a A |
29 | }; |
30 | ||
31 | const CFStringRef kSecIDSErrorDomain = CFSTR("com.apple.security.ids.error"); | |
fa7225c8 A |
32 | const CFStringRef kIDSOperationType = CFSTR("IDSMessageOperation"); |
33 | const CFStringRef kIDSMessageToSendKey = CFSTR("MessageToSendKey"); | |
5c19dc3a A |
34 | |
35 | // | |
36 | // V-table implementation forward declarations | |
37 | // | |
fa7225c8 | 38 | static bool sendToPeer(SOSTransportMessageRef transport, CFStringRef circleName, CFStringRef deviceID, CFStringRef peerID,CFDictionaryRef message, CFErrorRef *error); |
5c19dc3a A |
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); | |
45 | ||
46 | static inline CFIndex getTransportType(SOSTransportMessageRef transport, CFErrorRef *error){ | |
47 | return kIDS; | |
48 | } | |
49 | ||
fa7225c8 A |
50 | void SOSTransportMessageIDSSetFragmentationPreference(SOSTransportMessageRef transport, CFBooleanRef preference){ |
51 | SOSTransportMessageIDSRef t = (SOSTransportMessageIDSRef)transport; | |
52 | t->useFragmentation = preference; | |
53 | } | |
54 | ||
55 | CFBooleanRef SOSTransportMessageIDSGetFragmentationPreference(SOSTransportMessageRef transport){ | |
56 | SOSTransportMessageIDSRef t = (SOSTransportMessageIDSRef)transport; | |
57 | return t->useFragmentation; | |
58 | } | |
59 | ||
5c19dc3a A |
60 | SOSTransportMessageIDSRef SOSTransportMessageIDSCreate(SOSAccountRef account, CFStringRef circleName, CFErrorRef *error) |
61 | { | |
62 | SOSTransportMessageIDSRef ids = (SOSTransportMessageIDSRef) SOSTransportMessageCreateForSubclass(sizeof(struct __OpaqueSOSTransportMessageIDS) - sizeof(CFRuntimeBase), account, circleName, error); | |
63 | ||
64 | if (ids) { | |
65 | // Fill in vtable: | |
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; | |
73 | ||
74 | // Initialize ourselves | |
5c19dc3a | 75 | |
822b670c | 76 | SOSTransportMessageIDSGetIDSDeviceID(account); |
5c19dc3a A |
77 | SOSRegisterTransportMessage((SOSTransportMessageRef)ids); |
78 | } | |
79 | ||
80 | return ids; | |
81 | } | |
82 | static void destroy(SOSTransportMessageRef transport){ | |
83 | SOSUnregisterTransportMessage(transport); | |
84 | } | |
85 | ||
86 | static CF_RETURNS_RETAINED CFDictionaryRef handleMessages(SOSTransportMessageRef transport, CFMutableDictionaryRef circle_peer_messages_table, CFErrorRef *error) { | |
fa7225c8 A |
87 | return CFDictionaryCreateForCFTypes(kCFAllocatorDefault, NULL); |
88 | } | |
89 | ||
90 | static HandleIDSMessageReason checkMessageValidity(SOSAccountRef account, CFStringRef fromDeviceID, CFStringRef fromPeerID, CFStringRef *peerID, SOSPeerInfoRef *theirPeerInfo){ | |
91 | ||
92 | __block HandleIDSMessageReason reason = kHandleIDSMessageDontHandle; | |
93 | ||
94 | SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) { | |
95 | CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer); | |
96 | CFStringRef pID = SOSPeerInfoGetPeerID(peer); | |
97 | ||
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; | |
104 | return; | |
105 | } | |
106 | else if(CFStringCompare(fromDeviceID, deviceID, 0) != 0){ //IDSids do not match, ghost | |
107 | reason = kHandleIDSmessageDeviceIDMismatch; | |
108 | CFReleaseNull(deviceID); | |
109 | return; | |
110 | } | |
111 | else if(CFStringCompare(deviceID, fromDeviceID, 0) == 0){ | |
112 | *peerID = pID; | |
113 | *theirPeerInfo = peer; | |
114 | CFReleaseNull(deviceID); | |
115 | reason = kHandleIDSMessageSuccess; | |
116 | return; | |
117 | } | |
118 | } | |
119 | } | |
120 | CFReleaseNull(deviceID); | |
121 | }); | |
122 | ||
123 | return reason; | |
5c19dc3a A |
124 | } |
125 | ||
126 | HandleIDSMessageReason SOSTransportMessageIDSHandleMessage(SOSAccountRef account, CFDictionaryRef message, CFErrorRef *error) { | |
127 | ||
fa7225c8 | 128 | secnotice("IDS Transport", "SOSTransportMessageIDSHandleMessage!"); |
5c19dc3a A |
129 | |
130 | CFStringRef dataKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeyIDSDataMessage, kCFStringEncodingASCII); | |
131 | CFStringRef deviceIDKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeyDeviceID, kCFStringEncodingASCII); | |
fa7225c8 | 132 | CFStringRef sendersPeerIDKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeySendersPeerID, kCFStringEncodingASCII); |
5c19dc3a | 133 | |
fa7225c8 | 134 | HandleIDSMessageReason result = kHandleIDSMessageSuccess; |
5c19dc3a | 135 | |
fa7225c8 A |
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); | |
5c19dc3a | 139 | |
fa7225c8 A |
140 | CFStringRef peerID = NULL; |
141 | SOSPeerInfoRef theirPeer = NULL; | |
142 | ||
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); | |
147 | ||
148 | require_quiet((result = checkMessageValidity( account, fromDeviceID, fromPeerID, &peerID, &theirPeer)) == kHandleIDSMessageSuccess, exit); | |
149 | ||
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); | |
5c19dc3a | 155 | |
fa7225c8 A |
156 | //sync using fragmentation? |
157 | if(SOSPeerInfoShouldUseIDSMessageFragmentation(SOSFullPeerInfoGetPeerInfo(account->my_identity), theirPeer)){ | |
158 | //set useFragmentation bit | |
159 | SOSTransportMessageIDSSetFragmentationPreference(account->ids_message_transport, kCFBooleanTrue); | |
5c19dc3a A |
160 | } |
161 | else{ | |
fa7225c8 A |
162 | SOSTransportMessageIDSSetFragmentationPreference(account->ids_message_transport, kCFBooleanFalse); |
163 | } | |
164 | ||
165 | if(!SOSTransportMessageSyncWithPeers(account->ids_message_transport, peersToSyncWith, error)){ | |
166 | secerror("SOSTransportMessageIDSHandleMessage Could not sync with all peers: %@", *error); | |
167 | }else{ | |
168 | secnotice("IDS Transport", "Synced with all peers!"); | |
169 | } | |
170 | ||
171 | CFReleaseNull(peersToSyncWith); | |
172 | CFReleaseNull(peerIDs); | |
173 | }else{ | |
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; | |
180 | } | |
181 | secerror("IDS Transport Could not handle message: %@, %@", messageData, *error); | |
182 | CFReleaseNull(errorMessage); | |
183 | ||
184 | } | |
185 | else{ //no error but failed? drop it, log message | |
5c19dc3a | 186 | secerror("IDS Transport Could not handle message: %@", messageData); |
fa7225c8 A |
187 | result = kHandleIDSMessageDontHandle; |
188 | ||
5c19dc3a A |
189 | } |
190 | } | |
fa7225c8 A |
191 | |
192 | exit: | |
193 | CFReleaseNull(sendersPeerIDKey); | |
5c19dc3a A |
194 | CFReleaseNull(deviceIDKey); |
195 | CFReleaseNull(dataKey); | |
fa7225c8 | 196 | return result; |
5c19dc3a A |
197 | } |
198 | ||
199 | ||
e0e0d90e | 200 | static bool sendToPeer(SOSTransportMessageRef transport, CFStringRef circleName, CFStringRef deviceID, CFStringRef peerID,CFDictionaryRef message, CFErrorRef *error) |
5c19dc3a A |
201 | { |
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)); | |
fa7225c8 A |
210 | CFStringRef operationToString = NULL; |
211 | ||
212 | CFDictionaryRef messagetoSend = NULL; | |
5c19dc3a A |
213 | |
214 | require_action_quiet((deviceID != NULL && CFStringGetLength(deviceID) >0), fail, errorMessage = CFSTR("Need an IDS Device ID to sync")); | |
215 | ||
fa7225c8 A |
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); | |
223 | ||
224 | messagetoSend = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kIDSOperationType, operationToString, kIDSMessageToSendKey, message, NULL); | |
225 | } | |
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"); | |
229 | } | |
230 | ||
5c19dc3a A |
231 | dispatch_semaphore_t wait_for = dispatch_semaphore_create(0); |
232 | dispatch_retain(wait_for); // Both this scope and the block own it. | |
233 | ||
234 | secnotice("ids transport", "Starting"); | |
235 | ||
fa7225c8 | 236 | SOSCloudKeychainSendIDSMessage(messagetoSend, deviceID, ourPeerID, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), SOSTransportMessageIDSGetFragmentationPreference(transport), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) { |
5c19dc3a | 237 | success = (sync_error == NULL); |
e3d460c9 | 238 | if (sync_error && error) { |
5c19dc3a A |
239 | CFRetainAssign(*error, sync_error); |
240 | } | |
241 | ||
242 | dispatch_semaphore_signal(wait_for); | |
243 | dispatch_release(wait_for); | |
244 | }); | |
245 | ||
246 | dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER); | |
247 | dispatch_release(wait_for); | |
248 | ||
249 | if(!success){ | |
250 | if(error != NULL) | |
251 | secerror("Failed to send message to peer! %@", *error); | |
252 | else | |
253 | secerror("Failed to send message to peer"); | |
254 | } | |
255 | else{ | |
fa7225c8 | 256 | secnotice("IDS Transport", "Sent message to peer!"); |
5c19dc3a | 257 | } |
fa7225c8 A |
258 | |
259 | CFReleaseNull(messagetoSend); | |
5c19dc3a A |
260 | CFReleaseNull(operation); |
261 | CFReleaseNull(operationData); | |
262 | CFReleaseNull(mutableData); | |
fa7225c8 | 263 | CFReleaseNull(operationToString); |
5c19dc3a A |
264 | return success; |
265 | ||
266 | fail: | |
267 | userInfo = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, kCFErrorLocalizedDescriptionKey, errorMessage, NULL); | |
268 | if(error != NULL){ | |
269 | *error =CFErrorCreate(kCFAllocatorDefault, CFSTR("com.apple.security.ids.error"), kSecIDSErrorNoDeviceID, userInfo); | |
270 | secerror("%@", *error); | |
271 | } | |
fa7225c8 | 272 | CFReleaseNull(messagetoSend); |
5c19dc3a A |
273 | CFReleaseNull(operation); |
274 | CFReleaseNull(operationData); | |
275 | CFReleaseNull(mutableData); | |
276 | CFReleaseNull(userInfo); | |
fa7225c8 | 277 | CFReleaseNull(operationToString); |
5c19dc3a A |
278 | |
279 | return success; | |
280 | } | |
281 | ||
fa7225c8 | 282 | |
5c19dc3a A |
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; | |
286 | ||
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); | |
294 | } | |
295 | }); | |
296 | } | |
297 | }); | |
298 | ||
299 | return result; | |
300 | } | |
301 | ||
302 | static bool sendMessages(SOSTransportMessageRef transport, CFDictionaryRef circleToPeersToMessage, CFErrorRef *error) { | |
303 | __block bool result = true; | |
304 | SOSCircleRef circle = SOSAccountGetCircle(transport->account, error); | |
e3d460c9 A |
305 | SOSPeerInfoRef myPeer = SOSAccountGetMyPeerInfo(transport->account); |
306 | __block CFDictionaryRef message = NULL; | |
307 | __block CFStringRef peerID = NULL; | |
308 | require_quiet(myPeer, fail); | |
309 | ||
5c19dc3a A |
310 | |
311 | CFDictionaryForEach(circleToPeersToMessage, ^(const void *key, const void *value) { | |
312 | if (isString(key) && isDictionary(value)) { | |
313 | CFStringRef circleName = (CFStringRef) key; | |
e3d460c9 A |
314 | |
315 | CFDictionaryForEach(value, ^(const void *key1, const void *value1) { | |
316 | if (isString(key1) && isDictionary(value1)) { | |
317 | peerID = (CFStringRef) key1; | |
318 | message = CFRetainSafe((CFDictionaryRef) value1); | |
319 | } | |
320 | else{ | |
321 | peerID = (CFStringRef) key1; | |
322 | message = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, key1, value1, NULL); | |
323 | } | |
324 | SOSCircleForEachPeer(circle, ^(SOSPeerInfoRef peer) { | |
325 | if(!CFEqualSafe(myPeer, peer)){ | |
fa7225c8 A |
326 | if(SOSPeerInfoShouldUseIDSMessageFragmentation(myPeer, peer)){ |
327 | SOSTransportMessageIDSSetFragmentationPreference(transport, kCFBooleanTrue); | |
328 | } | |
329 | else{ | |
330 | SOSTransportMessageIDSSetFragmentationPreference(transport, kCFBooleanFalse); | |
331 | } | |
332 | ||
5c19dc3a | 333 | CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer); |
e3d460c9 | 334 | if(CFStringCompare(SOSPeerInfoGetPeerID(peer), peerID, 0) == 0){ |
5c19dc3a | 335 | bool rx = false; |
e0e0d90e | 336 | rx = sendToPeer(transport, circleName, deviceID, peerID, message, error); |
5c19dc3a A |
337 | result &= rx; |
338 | } | |
339 | CFReleaseNull(deviceID); | |
e3d460c9 A |
340 | } |
341 | }); | |
5c19dc3a A |
342 | }); |
343 | } | |
344 | }); | |
fa7225c8 | 345 | |
e3d460c9 | 346 | fail: |
fa7225c8 | 347 | CFReleaseNull(message); |
5c19dc3a A |
348 | return result; |
349 | } | |
350 | ||
fa7225c8 | 351 | |
5c19dc3a A |
352 | static bool flushChanges(SOSTransportMessageRef transport, CFErrorRef *error) |
353 | { | |
354 | return true; | |
355 | } | |
356 | ||
357 | static bool cleanupAfterPeer(SOSTransportMessageRef transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error) | |
358 | { | |
359 | return true; | |
360 | } | |
822b670c A |
361 | |
362 | void SOSTransportMessageIDSGetIDSDeviceID(SOSAccountRef account){ | |
363 | ||
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); | |
368 | if (!success) { | |
369 | secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", sync_error); | |
370 | } | |
371 | else{ | |
fa7225c8 | 372 | secnotice("IDS Transport", "Successfully attempting to retrieve the IDS Device ID"); |
822b670c A |
373 | } |
374 | }); | |
375 | } | |
376 | CFReleaseNull(deviceID); | |
377 | } |