]> git.saurik.com Git - apple/security.git/blob - KeychainSyncingOverIDSProxy/KeychainSyncingOverIDSProxy+ReceiveMessage.m
Security-57740.51.3.tar.gz
[apple/security.git] / KeychainSyncingOverIDSProxy / KeychainSyncingOverIDSProxy+ReceiveMessage.m
1 /*
2 * Copyright (c) 2012-2016 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 static NSString *const kIDSNumberOfFragments = @"NumberOfIDSMessageFragments";
53 static NSString *const kIDSFragmentIndex = @"kFragmentIndex";
54 static NSString *const kIDSOperationType = @"IDSMessageOperation";
55 static NSString *const kIDSMessageToSendKey = @"MessageToSendKey";
56 static NSString *const kIDSMessageUniqueID = @"MessageID";
57 static NSString *const kIDSMessageRecipientPeerID = @"RecipientPeerID";
58 static NSString *const kIDSMessageUseACKModel = @"UsesAckModel";
59
60 @implementation KeychainSyncingOverIDSProxy (ReceiveMessage)
61
62
63 -(int) countNumberOfValidObjects:(NSMutableArray*)fragmentsForDeviceID
64 {
65 __block int count = 0;
66 [fragmentsForDeviceID enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * _Nonnull stop) {
67 if(obj != [NSNull null]){
68 count++;
69 }
70 }];
71 return count;
72 }
73
74 -(BOOL) checkForFragmentation:(NSDictionary*)message id:(NSString*)fromID data:(NSData*)messageData
75 {
76 BOOL handOffMessage = false;
77
78 if([message valueForKey:kIDSNumberOfFragments] != nil){
79 NSNumber *idsNumberOfFragments = [message objectForKey:kIDSNumberOfFragments];
80 NSNumber *index = [message objectForKey:kIDSFragmentIndex];
81 NSString *uuidString = [message objectForKey:kIDSMessageUniqueID];
82
83 if([KeychainSyncingOverIDSProxy idsProxy].allFragmentedMessages == nil)
84 [KeychainSyncingOverIDSProxy idsProxy].allFragmentedMessages = [NSMutableDictionary dictionary];
85
86 NSMutableDictionary *uniqueMessages = [[KeychainSyncingOverIDSProxy idsProxy].allFragmentedMessages objectForKey: fromID];
87 if(uniqueMessages == nil)
88 uniqueMessages = [NSMutableDictionary dictionary];
89
90 NSMutableArray *fragmentsForDeviceID = [uniqueMessages objectForKey: uuidString];
91 if(fragmentsForDeviceID == nil){
92 fragmentsForDeviceID = [ [NSMutableArray alloc] initWithCapacity: [idsNumberOfFragments longValue]];
93 for (int i = 0; i <[idsNumberOfFragments longValue] ; i++) {
94 [fragmentsForDeviceID addObject:[NSNull null]];
95 }
96 }
97
98 [fragmentsForDeviceID replaceObjectAtIndex: [index intValue] withObject:messageData ];
99 [uniqueMessages setObject: fragmentsForDeviceID forKey:uuidString];
100 [[KeychainSyncingOverIDSProxy idsProxy].allFragmentedMessages setObject:uniqueMessages forKey: fromID];
101
102 if([self countNumberOfValidObjects:fragmentsForDeviceID] == [idsNumberOfFragments longValue])
103 handOffMessage = true;
104 else
105 handOffMessage = false;
106
107 }
108 else //no fragmentation in the message, ready to hand off to securityd
109 handOffMessage = true;
110
111 return handOffMessage;
112
113 }
114
115 -(NSMutableDictionary*) combineMessage:(NSString*)ID peerID:(NSString*)peerID uuid:(NSString*)uuid
116 {
117 NSString *dataKey = [ NSString stringWithUTF8String: kMessageKeyIDSDataMessage ];
118 NSString *deviceIDKey = [ NSString stringWithUTF8String: kMessageKeyDeviceID ];
119 NSString *peerIDKey = [ NSString stringWithUTF8String: kMessageKeyPeerID ];
120
121 NSMutableDictionary *arrayOfFragmentedMessagesByUUID = [[KeychainSyncingOverIDSProxy idsProxy].allFragmentedMessages objectForKey:ID];
122 NSMutableArray *messagesForUUID = [arrayOfFragmentedMessagesByUUID objectForKey:uuid];
123 NSMutableData* completeMessage = [NSMutableData data];
124
125 [messagesForUUID enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
126 NSData *messageFragment = (NSData*)obj;
127
128 [completeMessage appendData: messageFragment];
129 }];
130 //we've combined the message, now remove it from the fragmented messages dictionary
131 [arrayOfFragmentedMessagesByUUID removeObjectForKey:uuid];
132
133 return [NSMutableDictionary dictionaryWithObjectsAndKeys: completeMessage, dataKey, deviceID, deviceIDKey, peerID, peerIDKey, nil];
134 }
135
136 -(void) handleTestMessage:(NSString*)operation id:(NSString*)ID messageID:(NSString*)uniqueID senderPeerID:(NSString*)senderPeerID
137 {
138 int operationType = [operation intValue];
139 switch(operationType){
140 case kIDSPeerAvailabilityDone:
141 {
142 //set current timestamp to indicate success!
143 [self.peerNextSendCache setObject:[NSDate date] forKey:ID];
144
145 secnotice("IDS Transport","!received availability response!: %@", ID);
146 notify_post(kSOSCCPeerAvailable);
147 break;
148 }
149 case kIDSEndPingTestMessage:
150 secnotice("IDS Transport","received pong message from other device: %@, ping test PASSED", ID);
151 break;
152 case kIDSSendOneMessage:
153 secnotice("IDS Transport","received ping test message, dropping on the floor now");
154 break;
155
156 case kIDSPeerAvailability:
157 case kIDSStartPingTestMessage:
158 {
159 char* messageCharS;
160 if(operationType == kIDSPeerAvailability){
161 secnotice("IDS Transport","Received Availability Message from:%@!", ID);
162 asprintf(&messageCharS, "%d",kIDSPeerAvailabilityDone);
163 }
164 else{
165 secnotice("IDS Transport","Received PingTest Message from: %@!", ID);
166 asprintf(&messageCharS, "%d", kIDSEndPingTestMessage);
167 }
168
169 NSString *operationString = [[NSString alloc] initWithUTF8String:messageCharS];
170 NSString* messageString = @"peer availability check finished";
171 NSDictionary* messsageDictionary = @{kIDSOperationType:operationString, kIDSMessageToSendKey:messageString};
172
173 // We can always hold on to a message and our remote peers would bother everyone
174 [self sendIDSMessage:messsageDictionary name:ID peer:@"me"];
175
176 free(messageCharS);
177
178 break;
179 }
180 case kIDSPeerReceivedACK:
181 {
182 //set current timestamp to indicate success!
183 [self.peerNextSendCache setObject:[[NSDate alloc] init] forKey:ID];
184
185 //cancel timer!
186 secnotice("IDS Transport", "received ack for: %@", uniqueID);
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 [[KeychainSyncingOverIDSProxy idsProxy].messagesInFlight removeObjectForKey:uniqueID];
192 [[KeychainSyncingOverIDSProxy idsProxy] persistState];
193 }
194 //call out to securityd to set a NULL
195 [self sendKeysCallout:^NSMutableDictionary *(NSMutableDictionary *pending, NSError** error) {
196
197 CFErrorRef localError = NULL;
198 SOSCCClearPeerMessageKeyInKVS((__bridge CFStringRef)senderPeerID, &localError);
199 return NULL;
200 }];
201 break;
202 }
203 default:
204 break;
205 }
206 }
207
208 - (void)sendACK:(NSString*)ID peerID:(NSString*)sendersPeerID uniqueID:(NSString*)uniqueID
209 {
210 char* messageCharS;
211 NSString* messageString = @"ACK";
212
213 asprintf(&messageCharS, "%d",kIDSPeerReceivedACK);
214 NSString *operationString = [[NSString alloc] initWithUTF8String:messageCharS];
215
216 NSDictionary* messageDictionary = @{kIDSOperationType:operationString, kIDSMessageToSendKey:messageString, kIDSMessageUniqueID:uniqueID};
217
218 [self sendIDSMessage:messageDictionary name:ID peer:sendersPeerID];
219 free(messageCharS);
220
221 }
222
223 - (void)service:(IDSService *)service account:(IDSAccount *)account incomingMessage:(NSDictionary *)message fromID:(NSString *)fromID context:(IDSMessageContext *)context
224 {
225 NSString *dataKey = [ NSString stringWithUTF8String: kMessageKeyIDSDataMessage ];
226 NSString *deviceIDKey = [ NSString stringWithUTF8String: kMessageKeyDeviceID ];
227 NSString *peerIDKey = [ NSString stringWithUTF8String: kMessageKeyPeerID ];
228 NSString *sendersPeerIDKey = [NSString stringWithUTF8String: kMessageKeySendersPeerID];
229
230 dispatch_async(self.calloutQueue, ^{
231 NSString* messageID = nil;
232 NSString *ID = nil;
233 uint32_t operationType;
234 bool hadError = false;
235 CFStringRef errorMessage = NULL;
236 __block NSString* myPeerID = @"";
237 __block NSData *messageData = nil;
238 NSString* operationTypeAsString = nil;
239 NSMutableDictionary *messageDictionary = nil;
240 NSString *useAck = nil;
241
242
243 NSArray *devices = [self->_service devices];
244 for(NSUInteger i = 0; i < [ devices count ]; i++){
245 IDSDevice *device = devices[i];
246 if( [(IDSCopyIDForDevice(device)) containsString: fromID] == YES){
247 ID = device.uniqueID;
248 break;
249 }
250 }
251 secnotice("IDS Transport", "Received message from: %@: %@ ", ID, message);
252 NSString *sendersPeerID = [message objectForKey: sendersPeerIDKey];
253
254 if(sendersPeerID == nil)
255 sendersPeerID = [NSString string];
256
257
258 require_action_quiet(ID, fail, hadError = true; errorMessage = CFSTR("require the sender's device ID"));
259
260 operationTypeAsString = [message objectForKey: kIDSOperationType];
261 messageDictionary = [message objectForKey: kIDSMessageToSendKey];
262
263 messageID = [message objectForKey:kIDSMessageUniqueID];
264 useAck = [message objectForKey:kIDSMessageUseACKModel];
265
266 if(useAck != nil && [useAck compare:@"YES"] == NSOrderedSame)
267 require_quiet(messageID != nil, fail);
268
269 secnotice("IDS Transport","from peer %@, operation type as string: %@, as integer: %d", ID, operationTypeAsString, [operationTypeAsString intValue]);
270 operationType = [operationTypeAsString intValue];
271
272 if(operationType != kIDSKeychainSyncIDSFragmentation)
273 {
274 [self handleTestMessage:operationTypeAsString id:ID messageID:messageID senderPeerID:sendersPeerID];
275 }
276 else{
277
278 [messageDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
279 myPeerID = (NSString*)key;
280 messageData = (NSData*)obj;
281 }];
282
283 if(useAck != nil && [useAck compare:@"YES"] == NSOrderedSame)
284 [self sendACK:ID peerID:myPeerID uniqueID:messageID];
285
286 BOOL readyToHandOffToSecD = [self checkForFragmentation:message id:ID data:messageData];
287
288 NSMutableDictionary *messageAndFromID = nil;
289
290 if(readyToHandOffToSecD && ([message objectForKey:kIDSFragmentIndex])!= nil){
291 NSString* uuid = [message objectForKey:kIDSMessageUniqueID];
292 messageAndFromID = [self combineMessage:ID peerID:myPeerID uuid:uuid];
293 }
294 else if(readyToHandOffToSecD){
295 messageAndFromID = [NSMutableDictionary dictionaryWithObjectsAndKeys: messageData, dataKey, ID, deviceIDKey, myPeerID, peerIDKey, nil];
296 }
297 else
298 return;
299
300 //set the sender's peer id so we can check it in securityd
301 [messageAndFromID setObject:sendersPeerID forKey:sendersPeerIDKey];
302
303 if([KeychainSyncingOverIDSProxy idsProxy].isLocked){
304 //hang on to the message and set the retry deadline
305 [self.unhandledMessageBuffer setObject: messageAndFromID forKey: fromID];
306 }
307 else
308 [self sendMessageToSecurity:messageAndFromID fromID:fromID];
309 }
310
311 fail:
312 if(hadError)
313 secerror("error:%@", errorMessage);
314
315 });
316 }
317
318
319 - (void) handleAllPendingMessage
320 {
321 secnotice("IDS Transport", "Attempting to handle pending messsages");
322 if([self.unhandledMessageBuffer count] > 0){
323 secnotice("IDS Transport", "handling Message: %@", self.unhandledMessageBuffer);
324 NSMutableDictionary *copyOfUnhanlded = [NSMutableDictionary dictionaryWithDictionary:self.unhandledMessageBuffer];
325 [copyOfUnhanlded enumerateKeysAndObjectsUsingBlock: ^(id key, id obj, BOOL *stop)
326 {
327 NSMutableDictionary *messageAndFromID = (NSMutableDictionary*)obj;
328 NSString *fromID = (NSString*)key;
329 //remove the message from the official message buffer (if it fails to get handled it'll be reset again in sendMessageToSecurity)
330 [self.unhandledMessageBuffer removeObjectForKey: fromID];
331 [self sendMessageToSecurity:messageAndFromID fromID:fromID];
332 }];
333 }
334 }
335
336 - (bool) shouldPersistMessage:(NSDictionary*) newMessageAndFromID id:(NSString*)fromID
337 {
338 //get the dictionary of messages for a particular device id
339 NSDictionary* messagesFromBuffer = [self.unhandledMessageBuffer valueForKey:fromID];
340
341 if([messagesFromBuffer isEqual:newMessageAndFromID])
342 return false;
343
344 return true;
345 }
346
347 -(void)sendMessageToSecurity:(NSMutableDictionary*)messageAndFromID fromID:(NSString*)fromID
348 {
349 __block CFErrorRef cf_error = NULL;
350 __block HandleIDSMessageReason success = kHandleIDSMessageSuccess;
351
352 [self sendKeysCallout:^NSMutableDictionary *(NSMutableDictionary *pending, NSError** error) {
353
354 success = SOSCCHandleIDSMessage(((__bridge CFDictionaryRef)messageAndFromID), &cf_error);
355 //turns out the error needs to be evaluated as sync_and_do returns bools
356 if(cf_error != NULL)
357 {
358 if(CFErrorIsMalfunctioningKeybagError(cf_error)){
359 success = kHandleIDSMessageLocked;
360 }
361 }
362
363 if(success == kHandleIDSMessageLocked){
364 secnotice("IDS Transport","cannot handle messages from: %@ when locked, error:%@", fromID, cf_error);
365 if(!self.unhandledMessageBuffer)
366 self.unhandledMessageBuffer = [NSMutableDictionary dictionary];
367
368 //write message to disk if message is new to the unhandled queue
369 if([self shouldPersistMessage:messageAndFromID id:fromID])
370 [self persistState];
371
372 [self.unhandledMessageBuffer setObject: messageAndFromID forKey: fromID];
373 secnotice("IDS Transport", "unhandledMessageBuffer: %@", self.unhandledMessageBuffer);
374
375 return NULL;
376 }
377 else if(success == kHandleIDSMessageNotReady){
378 secnotice("IDS Transport","not ready to handle message from: %@, error:%@", fromID, cf_error);
379 if(!self.unhandledMessageBuffer)
380 self.unhandledMessageBuffer = [NSMutableDictionary dictionary];
381 [self.unhandledMessageBuffer setObject: messageAndFromID forKey: fromID];
382 secnotice("IDS Transport","unhandledMessageBuffer: %@", self.unhandledMessageBuffer);
383 //set timer
384 [[KeychainSyncingOverIDSProxy idsProxy] scheduleRetryRequestTimer];
385
386 //write message to disk if message is new to the unhandled queue
387 if([self shouldPersistMessage:messageAndFromID id:fromID])
388 [self persistState];
389
390 return NULL;
391 }
392 else if(success == kHandleIDSmessageDeviceIDMismatch){
393 secnotice("IDS Transport","message for a ghost! dropping message. error:%@", cf_error);
394 return NULL;
395 }
396 else if(success == kHandleIDSMessageDontHandle){
397 secnotice("IDS Transport","error in message, dropping message. error:%@", cf_error);
398 return NULL;
399 }
400 else{
401 secnotice("IDS Transport","IDSProxy handled this message %@, from: %@", messageAndFromID, fromID);
402 return (NSMutableDictionary*)messageAndFromID;
403 }
404
405 CFReleaseNull(cf_error);
406 }];
407 }
408
409 @end