]> git.saurik.com Git - apple/security.git/blame_incremental - OSX/sec/SOSCircle/SecureObjectSync/SOSTransportMessageIDS.c
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSTransportMessageIDS.c
... / ...
CommitLineData
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;
28 CFBooleanRef useFragmentation;
29};
30
31const CFStringRef kSecIDSErrorDomain = CFSTR("com.apple.security.ids.error");
32const CFStringRef kIDSOperationType = CFSTR("IDSMessageOperation");
33const CFStringRef kIDSMessageToSendKey = CFSTR("MessageToSendKey");
34
35//
36// V-table implementation forward declarations
37//
38static bool sendToPeer(SOSTransportMessageRef transport, CFStringRef circleName, CFStringRef deviceID, CFStringRef peerID,CFDictionaryRef message, CFErrorRef *error);
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
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
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
75
76 SOSTransportMessageIDSGetIDSDeviceID(account);
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) {
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;
124}
125
126HandleIDSMessageReason SOSTransportMessageIDSHandleMessage(SOSAccountRef account, CFDictionaryRef message, CFErrorRef *error) {
127
128 secnotice("IDS Transport", "SOSTransportMessageIDSHandleMessage!");
129
130 CFStringRef dataKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeyIDSDataMessage, kCFStringEncodingASCII);
131 CFStringRef deviceIDKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeyDeviceID, kCFStringEncodingASCII);
132 CFStringRef sendersPeerIDKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeySendersPeerID, kCFStringEncodingASCII);
133
134 HandleIDSMessageReason result = kHandleIDSMessageSuccess;
135
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);
139
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);
155
156 //sync using fragmentation?
157 if(SOSPeerInfoShouldUseIDSMessageFragmentation(SOSFullPeerInfoGetPeerInfo(account->my_identity), theirPeer)){
158 //set useFragmentation bit
159 SOSTransportMessageIDSSetFragmentationPreference(account->ids_message_transport, kCFBooleanTrue);
160 }
161 else{
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
186 secerror("IDS Transport Could not handle message: %@", messageData);
187 result = kHandleIDSMessageDontHandle;
188
189 }
190 }
191
192exit:
193 CFReleaseNull(sendersPeerIDKey);
194 CFReleaseNull(deviceIDKey);
195 CFReleaseNull(dataKey);
196 return result;
197}
198
199
200static bool sendToPeer(SOSTransportMessageRef transport, CFStringRef circleName, CFStringRef deviceID, CFStringRef peerID,CFDictionaryRef message, CFErrorRef *error)
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));
210 CFStringRef operationToString = NULL;
211
212 CFDictionaryRef messagetoSend = NULL;
213
214 require_action_quiet((deviceID != NULL && CFStringGetLength(deviceID) >0), fail, errorMessage = CFSTR("Need an IDS Device ID to sync"));
215
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
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
236 SOSCloudKeychainSendIDSMessage(messagetoSend, deviceID, ourPeerID, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), SOSTransportMessageIDSGetFragmentationPreference(transport), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
237 success = (sync_error == NULL);
238 if (sync_error && error) {
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{
256 secnotice("IDS Transport", "Sent message to peer!");
257 }
258
259 CFReleaseNull(messagetoSend);
260 CFReleaseNull(operation);
261 CFReleaseNull(operationData);
262 CFReleaseNull(mutableData);
263 CFReleaseNull(operationToString);
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 }
272 CFReleaseNull(messagetoSend);
273 CFReleaseNull(operation);
274 CFReleaseNull(operationData);
275 CFReleaseNull(mutableData);
276 CFReleaseNull(userInfo);
277 CFReleaseNull(operationToString);
278
279 return success;
280}
281
282
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);
305 SOSPeerInfoRef myPeer = SOSAccountGetMyPeerInfo(transport->account);
306 __block CFDictionaryRef message = NULL;
307 __block CFStringRef peerID = NULL;
308 require_quiet(myPeer, fail);
309
310
311 CFDictionaryForEach(circleToPeersToMessage, ^(const void *key, const void *value) {
312 if (isString(key) && isDictionary(value)) {
313 CFStringRef circleName = (CFStringRef) key;
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)){
326 if(SOSPeerInfoShouldUseIDSMessageFragmentation(myPeer, peer)){
327 SOSTransportMessageIDSSetFragmentationPreference(transport, kCFBooleanTrue);
328 }
329 else{
330 SOSTransportMessageIDSSetFragmentationPreference(transport, kCFBooleanFalse);
331 }
332
333 CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
334 if(CFStringCompare(SOSPeerInfoGetPeerID(peer), peerID, 0) == 0){
335 bool rx = false;
336 rx = sendToPeer(transport, circleName, deviceID, peerID, message, error);
337 result &= rx;
338 }
339 CFReleaseNull(deviceID);
340 }
341 });
342 });
343 }
344 });
345
346fail:
347 CFReleaseNull(message);
348 return result;
349}
350
351
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}
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{
372 secnotice("IDS Transport", "Successfully attempting to retrieve the IDS Device ID");
373 }
374 });
375 }
376 CFReleaseNull(deviceID);
377}