]> git.saurik.com Git - apple/security.git/blob - KeychainSyncingOverIDSProxy/KeychainSyncingOverIDSProxy+ReceiveMessage.m
Security-58286.51.6.tar.gz
[apple/security.git] / KeychainSyncingOverIDSProxy / KeychainSyncingOverIDSProxy+ReceiveMessage.m
1 /*
2 * Copyright (c) 2012-2017 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 #import <Foundation/Foundation.h>
26 #import <Foundation/NSArray.h>
27 #import <Foundation/Foundation.h>
28
29 #import <Security/SecBasePriv.h>
30 #import <Security/SecItemPriv.h>
31 #import <utilities/debugging.h>
32 #import <notify.h>
33
34 #include <Security/CKBridge/SOSCloudKeychainConstants.h>
35 #include <Security/SecureObjectSync/SOSARCDefines.h>
36 #include <Security/SecureObjectSync/SOSCloudCircle.h>
37 #include <Security/SecureObjectSync/SOSCloudCircleInternal.h>
38 #include <utilities/SecCFWrappers.h>
39
40 #import <IDS/IDS.h>
41 #import <os/activity.h>
42
43 #include <utilities/SecAKSWrappers.h>
44 #include <utilities/SecCFRelease.h>
45 #include <AssertMacros.h>
46
47 #import "IDSPersistentState.h"
48 #import "KeychainSyncingOverIDSProxy+ReceiveMessage.h"
49 #import "KeychainSyncingOverIDSProxy+SendMessage.h"
50 #import "IDSProxy.h"
51
52
53 static NSString *const kIDSNumberOfFragments = @"NumberOfIDSMessageFragments";
54 static NSString *const kIDSFragmentIndex = @"kFragmentIndex";
55 static NSString *const kIDSMessageRecipientID = @"RecipientPeerID";
56 static NSString *const kIDSMessageUseACKModel = @"UsesAckModel";
57 static NSString *const kIDSMessageSendersDeviceID = @"SendersDeviceID";
58
59 @implementation KeychainSyncingOverIDSProxy (ReceiveMessage)
60
61 -(int) countNumberOfValidObjects:(NSMutableArray*)fragmentsForDeviceID
62 {
63 __block int count = 0;
64 [fragmentsForDeviceID enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * _Nonnull stop) {
65 if(obj != [NSNull null]){
66 count++;
67 }
68 }];
69 return count;
70 }
71
72 -(BOOL) checkForFragmentation:(NSDictionary*)message id:(NSString*)fromID data:(NSData*)messageData
73 {
74 BOOL handOffMessage = false;
75
76 if([message valueForKey:kIDSNumberOfFragments] != nil){
77 NSNumber *idsNumberOfFragments = [message objectForKey:kIDSNumberOfFragments];
78 NSNumber *index = [message objectForKey:kIDSFragmentIndex];
79 NSString *uuidString = [message objectForKey:(__bridge NSString*)kIDSMessageUniqueID];
80
81 if([KeychainSyncingOverIDSProxy idsProxy].allFragmentedMessages == nil)
82 [KeychainSyncingOverIDSProxy idsProxy].allFragmentedMessages = [NSMutableDictionary dictionary];
83
84 NSMutableDictionary *uniqueMessages = [[KeychainSyncingOverIDSProxy idsProxy].allFragmentedMessages objectForKey: fromID];
85 if(uniqueMessages == nil)
86 uniqueMessages = [NSMutableDictionary dictionary];
87
88 NSMutableArray *fragmentsForDeviceID = [uniqueMessages objectForKey: uuidString];
89 if(fragmentsForDeviceID == nil){
90 fragmentsForDeviceID = [ [NSMutableArray alloc] initWithCapacity: [idsNumberOfFragments longValue]];
91 for (int i = 0; i <[idsNumberOfFragments longValue] ; i++) {
92 [fragmentsForDeviceID addObject:[NSNull null]];
93 }
94 }
95
96 [fragmentsForDeviceID replaceObjectAtIndex: [index intValue] withObject:messageData ];
97 [uniqueMessages setObject: fragmentsForDeviceID forKey:uuidString];
98 [[KeychainSyncingOverIDSProxy idsProxy].allFragmentedMessages setObject:uniqueMessages forKey: fromID];
99
100 if([self countNumberOfValidObjects:fragmentsForDeviceID] == [idsNumberOfFragments longValue])
101 handOffMessage = true;
102 else
103 handOffMessage = false;
104
105 }
106 else //no fragmentation in the message, ready to hand off to securityd
107 handOffMessage = true;
108
109 return handOffMessage;
110
111 }
112
113 -(NSMutableDictionary*) combineMessage:(NSString*)ID peerID:(NSString*)peerID uuid:(NSString*)uuid
114 {
115 NSString *dataKey = [ NSString stringWithUTF8String: kMessageKeyIDSDataMessage ];
116 NSString *deviceIDKey = [ NSString stringWithUTF8String: kMessageKeyDeviceID ];
117 NSString *peerIDKey = [ NSString stringWithUTF8String: kMessageKeyPeerID ];
118
119 NSMutableDictionary *arrayOfFragmentedMessagesByUUID = [[KeychainSyncingOverIDSProxy idsProxy].allFragmentedMessages objectForKey:ID];
120 NSMutableArray *messagesForUUID = [arrayOfFragmentedMessagesByUUID objectForKey:uuid];
121 NSMutableData* completeMessage = [NSMutableData data];
122
123 [messagesForUUID enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
124 NSData *messageFragment = (NSData*)obj;
125
126 [completeMessage appendData: messageFragment];
127 }];
128 //we've combined the message, now remove it from the fragmented messages dictionary
129 [arrayOfFragmentedMessagesByUUID removeObjectForKey:uuid];
130
131 return [NSMutableDictionary dictionaryWithObjectsAndKeys: completeMessage, dataKey, ID, deviceIDKey, peerID, peerIDKey, nil];
132 }
133
134 -(void) handleTestMessage:(NSString*)operation id:(NSString*)ID messageID:(NSString*)uniqueID senderPeerID:(NSString*)senderPeerID
135 {
136 int operationType = [operation intValue];
137 switch(operationType){
138 case kIDSPeerAvailabilityDone:
139 {
140 //set current timestamp to indicate success!
141 [self.peerNextSendCache setObject:[NSDate date] forKey:ID];
142
143 secnotice("IDS Transport","!received availability response!: %@", ID);
144 notify_post(kSOSCCPeerAvailable);
145 break;
146 }
147 case kIDSEndPingTestMessage:
148 secnotice("IDS Transport","received pong message from other device: %@, ping test PASSED", ID);
149 break;
150 case kIDSSendOneMessage:
151 secnotice("IDS Transport","received ping test message, dropping on the floor now");
152 break;
153
154 case kIDSPeerAvailability:
155 case kIDSStartPingTestMessage:
156 {
157 char* messageCharS;
158 if(operationType == kIDSPeerAvailability){
159 secnotice("IDS Transport","Received Availability Message from:%@!", ID);
160 asprintf(&messageCharS, "%d",kIDSPeerAvailabilityDone);
161 }
162 else{
163 secnotice("IDS Transport","Received PingTest Message from: %@!", ID);
164 asprintf(&messageCharS, "%d", kIDSEndPingTestMessage);
165 }
166
167 NSString *operationString = [[NSString alloc] initWithUTF8String:messageCharS];
168 NSString* messageString = @"peer availability check finished";
169 NSDictionary* messsageDictionary = @{(__bridge NSString*)kIDSOperationType:operationString, (__bridge NSString*)kIDSMessageToSendKey:messageString};
170
171 // We can always hold on to a message and our remote peers would bother everyone
172 [self sendIDSMessage:messsageDictionary name:ID peer:@"me" senderDeviceID:NULL];
173
174 free(messageCharS);
175
176 break;
177 }
178 case kIDSPeerReceivedACK:
179 {
180 //set current timestamp to indicate success!
181 [self.peerNextSendCache setObject:[[NSDate alloc] init] forKey:ID];
182
183 //cancel timer!
184 secnotice("IDS Transport", "received ack for: %@", uniqueID);
185 dispatch_async(self.pingQueue, ^{
186 //remove timer for message id
187 dispatch_source_t timer = [[KeychainSyncingOverIDSProxy idsProxy].pingTimers objectForKey:uniqueID];
188 if(timer != nil){
189 dispatch_cancel(timer);
190 [[KeychainSyncingOverIDSProxy idsProxy].pingTimers removeObjectForKey:uniqueID];
191 dispatch_sync(self.dataQueue, ^{
192 [[KeychainSyncingOverIDSProxy idsProxy].messagesInFlight removeObjectForKey:uniqueID];
193 });
194 [[KeychainSyncingOverIDSProxy idsProxy] persistState];
195 }
196 });
197 //call out to securityd to set a NULL
198 [self sendKeysCallout:^NSMutableDictionary *(NSMutableDictionary *pending, NSError** error) {
199
200 CFErrorRef localError = NULL;
201 SOSCCClearPeerMessageKeyInKVS((__bridge CFStringRef)senderPeerID, &localError);
202 return NULL;
203 }];
204 break;
205 }
206 default:
207 break;
208 }
209 }
210
211 - (void)sendACK:(NSString*)ID peerID:(NSString*)sendersPeerID uniqueID:(NSString*)uniqueID senderDeviceID:(NSString*)senderDeviceID
212 {
213 char* messageCharS;
214 NSString* messageString = @"ACK";
215
216 asprintf(&messageCharS, "%d",kIDSPeerReceivedACK);
217 NSString *operationString = [[NSString alloc] initWithUTF8String:messageCharS];
218
219 NSDictionary* messageDictionary = @{(__bridge NSString*)kIDSOperationType:operationString, (__bridge NSString*)kIDSMessageToSendKey:messageString, (__bridge NSString*)kIDSMessageUniqueID:uniqueID};
220
221 [self sendIDSMessage:messageDictionary name:ID peer:sendersPeerID senderDeviceID:senderDeviceID];
222
223 free(messageCharS);
224
225 }
226
227 - (void)updateDeviceList
228 {
229 self.deviceIDFromAuthToken = nil;
230 self.deviceIDFromAuthToken = [NSMutableDictionary dictionary];
231 [self calloutWith:^(NSMutableDictionary *pending, bool handlePendingMesssages, bool doSetDeviceID, dispatch_queue_t queue, void(^done)(NSMutableDictionary *, bool, bool)) {
232 }];
233 }
234 - (void)service:(IDSService *)service account:(IDSAccount *)account incomingMessage:(NSDictionary *)message fromID:(NSString *)fromID context:(IDSMessageContext *)context
235 {
236 NSString *dataKey = [ NSString stringWithUTF8String: kMessageKeyIDSDataMessage ];
237 NSString *deviceIDKey = [ NSString stringWithUTF8String: kMessageKeyDeviceID ];
238 NSString *peerIDKey = [ NSString stringWithUTF8String: kMessageKeyPeerID ];
239 NSString *sendersPeerIDKey = [NSString stringWithUTF8String: kMessageKeySendersPeerID];
240
241 [KeychainSyncingOverIDSProxy idsProxy].incomingMessages++;
242
243 dispatch_async(self.calloutQueue, ^{
244 NSString* messageID = nil;
245 uint32_t operationType;
246 bool hadError = false;
247 CFStringRef errorMessage = NULL;
248 __block NSString* myPeerID = @"";
249 __block NSData *messageData = nil;
250 NSString* operationTypeAsString = nil;
251 NSMutableDictionary *messageDictionary = nil;
252 NSString *useAck = nil;
253 NSString *senderDeviceID = nil;
254 NSArray *devices = [self->_service devices];
255 for(NSUInteger i = 0; i < [ devices count ]; i++){
256 IDSDevice *device = devices[i];
257 if( [(IDSCopyIDForDevice(device)) containsString: fromID] == YES){
258 senderDeviceID = device.uniqueID;
259 break;
260 }
261 }
262 [[KeychainSyncingOverIDSProxy idsProxy] printMessage:message state:[NSString stringWithFormat:@"received message from: %@", senderDeviceID]];
263 NSString *sendersPeerID = [message objectForKey: sendersPeerIDKey];
264
265 if(sendersPeerID == nil)
266 sendersPeerID = [NSString string];
267
268 if(!senderDeviceID){
269 senderDeviceID = message[kIDSMessageSendersDeviceID];
270 secnotice("IDS Transport", "Their device ID!: %@", senderDeviceID);
271 }
272 require_action_quiet(senderDeviceID, fail, hadError = true; errorMessage = CFSTR("require the sender's device ID"));
273
274 operationTypeAsString = [message objectForKey: (__bridge NSString*)kIDSOperationType];
275 messageDictionary = [message objectForKey: (__bridge NSString*)kIDSMessageToSendKey];
276
277 messageID = [message objectForKey:(__bridge NSString*)kIDSMessageUniqueID];
278 useAck = [message objectForKey:kIDSMessageUseACKModel];
279
280 if(useAck != nil && [useAck compare:@"YES"] == NSOrderedSame)
281 require_quiet(messageID != nil, fail);
282
283 secnotice("IDS Transport","from peer %@, operation: %@", senderDeviceID, operationTypeAsString);
284 operationType = [operationTypeAsString intValue];
285
286 if(operationType != kIDSKeychainSyncIDSFragmentation)
287 {
288 [self handleTestMessage:operationTypeAsString id:senderDeviceID messageID:messageID senderPeerID:sendersPeerID];
289 }
290 else{
291
292 [messageDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
293 myPeerID = (NSString*)key;
294 messageData = (NSData*)obj;
295 }];
296
297 BOOL readyToHandOffToSecD = [self checkForFragmentation:message id:senderDeviceID data:messageData];
298
299 NSMutableDictionary *messageAndFromID = nil;
300
301 if(readyToHandOffToSecD && ([message objectForKey:kIDSFragmentIndex])!= nil){
302 secnotice("IDS Transport", "combing message");
303 NSString* uuid = [message objectForKey:(__bridge NSString*)kIDSMessageUniqueID];
304 messageAndFromID = [self combineMessage:senderDeviceID peerID:myPeerID uuid:uuid];
305 //update next sequence number
306 }
307 else if(readyToHandOffToSecD){
308 messageAndFromID = [NSMutableDictionary dictionaryWithObjectsAndKeys: messageData, dataKey, senderDeviceID, deviceIDKey, myPeerID, peerIDKey, nil];
309 }
310 else
311 return;
312
313 //set the sender's peer id so we can check it in securityd
314 [messageAndFromID setObject:sendersPeerID forKey:sendersPeerIDKey];
315
316 if([KeychainSyncingOverIDSProxy idsProxy].isLocked){
317 //hang on to the message and set the retry deadline
318 dispatch_sync(self.dataQueue, ^{
319 [self.unhandledMessageBuffer setObject: messageAndFromID forKey: fromID];
320 });
321 }
322 else{
323 [self sendMessageToSecurity:messageAndFromID fromID:fromID shouldSendAck:useAck peerID:myPeerID messageID:messageID deviceID:senderDeviceID];
324
325 }
326 }
327
328 fail:
329 if(hadError)
330 secerror("error:%@", errorMessage);
331 });
332 }
333
334
335 - (void) handleAllPendingMessage
336 {
337 secnotice("IDS Transport", "Attempting to handle pending messsages");
338
339 NSMutableDictionary * __block copyOfUnhandled = nil;
340 dispatch_sync(self.dataQueue, ^{
341 if ([self.unhandledMessageBuffer count] > 0) {
342 secnotice("IDS Transport", "handling messages: %@", self.unhandledMessageBuffer);
343 copyOfUnhandled = [NSMutableDictionary dictionaryWithDictionary:self.unhandledMessageBuffer];
344 }
345 });
346
347 [copyOfUnhandled enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop)
348 {
349 NSMutableDictionary *messageAndFromID = (NSMutableDictionary*)obj;
350 NSString *fromID = (NSString*)key;
351 //remove the message from the official message buffer (if it fails to get handled it'll be reset again in sendMessageToSecurity)
352 dispatch_sync(self.dataQueue, ^{
353 [self.unhandledMessageBuffer removeObjectForKey: fromID];
354 });
355 [self sendMessageToSecurity:messageAndFromID fromID:fromID shouldSendAck:nil peerID:nil messageID:nil deviceID:nil];
356 }];
357 }
358
359 - (bool) shouldPersistMessage:(NSDictionary*) newMessageAndFromID id:(NSString*)fromID
360 {
361 //get the dictionary of messages for a particular device id
362 NSDictionary* __block messagesFromBuffer;
363 dispatch_sync(self.dataQueue, ^{
364 messagesFromBuffer = [self.unhandledMessageBuffer valueForKey:fromID];
365 });
366
367 if([messagesFromBuffer isEqual:newMessageAndFromID])
368 return false;
369
370 return true;
371 }
372
373 -(void)sendMessageToSecurity:(NSMutableDictionary*)messageAndFromID fromID:(NSString*)fromID shouldSendAck:(NSString *)useAck peerID:(NSString*)peerID messageID:(NSString*)messageID deviceID:(NSString*)senderDeviceID
374 {
375 __block CFErrorRef cf_error = NULL;
376 __block HandleIDSMessageReason success = kHandleIDSMessageSuccess;
377
378 [self sendKeysCallout:^NSMutableDictionary *(NSMutableDictionary *pending, NSError** error) {
379
380 success = SOSCCHandleIDSMessage(((__bridge CFDictionaryRef)messageAndFromID), &cf_error);
381 //turns out the error needs to be evaluated as sync_and_do returns bools
382 if(cf_error != NULL)
383 {
384 if(CFErrorIsMalfunctioningKeybagError(cf_error)){
385 success = kHandleIDSMessageLocked;
386 }
387 }
388
389 if(success == kHandleIDSMessageLocked){
390 secnotice("IDS Transport","cannot handle messages from: %@ when locked, error:%@", fromID, cf_error);
391 // I don't think this is ever nil but it was like this when I got here
392 dispatch_sync(self.dataQueue, ^{
393 if(!self.unhandledMessageBuffer) {
394 self.unhandledMessageBuffer = [NSMutableDictionary dictionary];
395 }
396 });
397
398 //write message to disk if message is new to the unhandled queue
399 if([self shouldPersistMessage:messageAndFromID id:fromID]) {
400 [self persistState];
401 }
402
403 dispatch_sync(self.dataQueue, ^{
404 [self.unhandledMessageBuffer setObject: messageAndFromID forKey: fromID];
405 secnotice("IDS Transport", "unhandledMessageBuffer: %@", self.unhandledMessageBuffer);
406 });
407
408 return NULL;
409 }
410 else if(success == kHandleIDSMessageNotReady){
411 secnotice("IDS Transport","not ready to handle message from: %@, error:%@", fromID, cf_error);
412 dispatch_sync(self.dataQueue, ^{
413 if(!self.unhandledMessageBuffer) {
414 self.unhandledMessageBuffer = [NSMutableDictionary dictionary];
415 }
416 [self.unhandledMessageBuffer setObject: messageAndFromID forKey: fromID];
417 secnotice("IDS Transport","unhandledMessageBuffer: %@", self.unhandledMessageBuffer);
418 });
419 //write message to disk if message is new to the unhandled queue
420 if([self shouldPersistMessage:messageAndFromID id:fromID])
421 [self persistState];
422
423 [[KeychainSyncingOverIDSProxy idsProxy] scheduleRetryRequestTimer];
424 return NULL;
425 }
426 else if(success == kHandleIDSmessageDeviceIDMismatch){
427 secnotice("IDS Transport","message for a ghost! dropping message. error:%@", cf_error);
428 return NULL;
429 }
430 else if(success == kHandleIDSMessageDontHandle){
431 secnotice("IDS Transport","error in message, dropping message. error:%@", cf_error);
432 return NULL;
433 }
434 else{
435 secnotice("IDS Transport","IDSProxy handled this message %@, from: %@", messageAndFromID, fromID);
436
437 if(useAck != nil && [useAck compare:@"YES"] == NSOrderedSame)
438 [self sendACK:senderDeviceID peerID:peerID uniqueID:messageID senderDeviceID:senderDeviceID];
439 return (NSMutableDictionary*)messageAndFromID;
440 }
441
442 CFReleaseNull(cf_error);
443 }];
444 }
445
446 @end