]> git.saurik.com Git - apple/security.git/blame - OSX/sec/SOSCircle/SecureObjectSync/SOSTransportMessageIDS.c
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSTransportMessageIDS.c
CommitLineData
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
26struct __OpaqueSOSTransportMessageIDS {
27 struct __OpaqueSOSTransportMessage m;
fa7225c8 28 CFBooleanRef useFragmentation;
5c19dc3a
A
29};
30
31const CFStringRef kSecIDSErrorDomain = CFSTR("com.apple.security.ids.error");
fa7225c8
A
32const CFStringRef kIDSOperationType = CFSTR("IDSMessageOperation");
33const CFStringRef kIDSMessageToSendKey = CFSTR("MessageToSendKey");
5c19dc3a
A
34
35//
36// V-table implementation forward declarations
37//
fa7225c8 38static bool sendToPeer(SOSTransportMessageRef transport, CFStringRef circleName, CFStringRef deviceID, CFStringRef peerID,CFDictionaryRef message, CFErrorRef *error);
5c19dc3a
A
39static bool syncWithPeers(SOSTransportMessageRef transport, CFDictionaryRef circleToPeerIDs, CFErrorRef *error);
40static bool sendMessages(SOSTransportMessageRef transport, CFDictionaryRef circleToPeersToMessage, CFErrorRef *error);
41static void destroy(SOSTransportMessageRef transport);
42static bool cleanupAfterPeer(SOSTransportMessageRef transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error);
43static bool flushChanges(SOSTransportMessageRef transport, CFErrorRef *error);
44static CF_RETURNS_RETAINED CFDictionaryRef handleMessages(SOSTransportMessageRef transport, CFMutableDictionaryRef circle_peer_messages_table, CFErrorRef *error);
45
46static inline CFIndex getTransportType(SOSTransportMessageRef transport, CFErrorRef *error){
47 return kIDS;
48}
49
fa7225c8
A
50void SOSTransportMessageIDSSetFragmentationPreference(SOSTransportMessageRef transport, CFBooleanRef preference){
51 SOSTransportMessageIDSRef t = (SOSTransportMessageIDSRef)transport;
52 t->useFragmentation = preference;
53}
54
55CFBooleanRef SOSTransportMessageIDSGetFragmentationPreference(SOSTransportMessageRef transport){
56 SOSTransportMessageIDSRef t = (SOSTransportMessageIDSRef)transport;
57 return t->useFragmentation;
58 }
59
5c19dc3a
A
60SOSTransportMessageIDSRef 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}
82static void destroy(SOSTransportMessageRef transport){
83 SOSUnregisterTransportMessage(transport);
84}
85
86static CF_RETURNS_RETAINED CFDictionaryRef handleMessages(SOSTransportMessageRef transport, CFMutableDictionaryRef circle_peer_messages_table, CFErrorRef *error) {
fa7225c8
A
87 return CFDictionaryCreateForCFTypes(kCFAllocatorDefault, NULL);
88}
89
90static 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
126HandleIDSMessageReason 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
192exit:
193 CFReleaseNull(sendersPeerIDKey);
5c19dc3a
A
194 CFReleaseNull(deviceIDKey);
195 CFReleaseNull(dataKey);
fa7225c8 196 return result;
5c19dc3a
A
197}
198
199
e0e0d90e 200static 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
266fail:
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
283static 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
302static 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 346fail:
fa7225c8 347 CFReleaseNull(message);
5c19dc3a
A
348 return result;
349}
350
fa7225c8 351
5c19dc3a
A
352static bool flushChanges(SOSTransportMessageRef transport, CFErrorRef *error)
353{
354 return true;
355}
356
357static bool cleanupAfterPeer(SOSTransportMessageRef transport, CFDictionaryRef circle_to_peer_ids, CFErrorRef *error)
358{
359 return true;
360}
822b670c
A
361
362void 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}