]> git.saurik.com Git - apple/security.git/blob - IDSKeychainSyncingProxy/IDSKeychainSyncingProxy+IDSProxySendMessage.m
Security-57740.31.2.tar.gz
[apple/security.git] / IDSKeychainSyncingProxy / IDSKeychainSyncingProxy+IDSProxySendMessage.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/NSArray.h>
26 #import <Foundation/Foundation.h>
27
28 #import <Security/SecBasePriv.h>
29 #import <Security/SecItemPriv.h>
30 #import <utilities/debugging.h>
31 #import <notify.h>
32
33 #include <Security/CKBridge/SOSCloudKeychainConstants.h>
34 #include <Security/SecureObjectSync/SOSARCDefines.h>
35 #include <Security/SecureObjectSync/SOSCloudCircle.h>
36 #include <Security/SecureObjectSync/SOSCloudCircleInternal.h>
37
38 #import <IDS/IDS.h>
39 #import <os/activity.h>
40
41 #include <utilities/SecAKSWrappers.h>
42 #include <utilities/SecCFRelease.h>
43 #include <AssertMacros.h>
44
45 #import "IDSProxy.h"
46 #import "IDSPersistentState.h"
47 #import "IDSKeychainSyncingProxy+IDSProxySendMessage.h"
48 #import "IDSKeychainSyncingProxy+IDSProxyThrottle.h"
49
50 #define kSecServerKeychainChangedNotification "com.apple.security.keychainchanged"
51
52
53 static NSString *const IDSSendMessageOptionForceEncryptionOffKey = @"IDSSendMessageOptionForceEncryptionOff";
54
55 static NSString *const kIDSNumberOfFragments = @"NumberOfIDSMessageFragments";
56 static NSString *const kIDSFragmentIndex = @"kFragmentIndex";
57 static NSString *const kIDSOperationType = @"IDSMessageOperation";
58 static NSString *const kIDSMessageToSendKey = @"MessageToSendKey";
59 static NSString *const kIDSMessageUniqueID = @"MessageID";
60 static const int64_t kRetryTimerLeeway = (NSEC_PER_MSEC * 250); // 250ms leeway for handling unhandled messages.
61 static const int64_t timeout = 7;
62
63 static const int64_t kMaxIDSMessagePayloadSize = 64000;
64
65
66 @implementation IDSKeychainSyncingProxy (IDSProxySendMessage)
67
68 -(bool) chunkAndSendKeychainPayload:(NSMutableData*)keychainData deviceID:(NSString*)deviceName ourPeerID:(NSString*)ourPeerID theirPeerID:(NSString*) theirPeerID operation:(NSString*)operationTypeAsString error:(NSError**) error
69 {
70 __block BOOL result = false;
71
72 CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
73 CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuid);
74
75 uint64_t keychainDataLength = (uint64_t)[keychainData length];
76 NSUInteger tempLength = [keychainData length];
77 int fragmentIndex = 0;
78 int startingPosition = 0;
79
80 int totalNumberOfFragments = ceil((double)((double)keychainDataLength/(double)kMaxIDSMessagePayloadSize));
81 secnotice("IDS Transport","Total number of Fragments: %d", totalNumberOfFragments);
82
83 while(tempLength != 0){
84 secnotice("IDS Transport","length: %lu", (unsigned long)tempLength);
85 NSUInteger endlength;
86 if(tempLength < kMaxIDSMessagePayloadSize)
87 endlength = tempLength;
88 else
89 endlength = kMaxIDSMessagePayloadSize;
90
91 NSData *fragment = [keychainData subdataWithRange:NSMakeRange(startingPosition, endlength)];
92 NSMutableDictionary *newFragmentDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:fragment, theirPeerID, nil];
93
94 NSMutableDictionary* newMessageFragment = [NSMutableDictionary dictionaryWithObjectsAndKeys:deviceName, @"deviceID",
95 [[NSNumber alloc]initWithInt: totalNumberOfFragments], kIDSNumberOfFragments,
96 [[NSNumber alloc] initWithInt: fragmentIndex], kIDSFragmentIndex,
97 newFragmentDictionary,kIDSMessageToSendKey,
98 operationTypeAsString, kIDSOperationType,
99 (__bridge NSString*)uuidString, kIDSMessageUniqueID, nil];
100 NSString *identifier = [NSString string];
101
102 secnotice("IDS Transport","sending fragment: %@", newMessageFragment);
103 result = [self sendIDSMessage:newMessageFragment name:deviceName peer:ourPeerID identifier:&identifier error:error];
104 startingPosition+=endlength;
105 tempLength -= endlength;
106 fragmentIndex++;
107 }
108 CFReleaseNull(uuidString);
109 CFReleaseNull(uuid);
110 return result;
111 }
112
113 -(BOOL) sendFragmentedIDSMessages:(NSDictionary*)data name:(NSString*) deviceName peer:(NSString*) ourPeerID error:(NSError**) error
114 {
115
116 __block BOOL result = false;
117
118 __block NSMutableData *keychainData = nil;
119 __block NSString *theirPeerID = nil;
120 secnotice("IDS Transport","fragmenting message! %@", data);
121 NSString *identifier = [NSString string];
122
123 NSString* operationTypeAsString = [data objectForKey: kIDSOperationType];
124 NSMutableDictionary *messageDictionary = [data objectForKey: kIDSMessageToSendKey];
125
126 if([operationTypeAsString intValue] == kIDSKeychainSyncIDSFragmentation){
127
128 [messageDictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
129 keychainData = (NSMutableData*)obj;
130 theirPeerID = (NSString*)key;
131 return;
132 }];
133 secnotice("IDS Transport","keychainData length: %lu", (unsigned long)[keychainData length]);
134 if((uint64_t)[keychainData length] >= kMaxIDSMessagePayloadSize){
135 [self chunkAndSendKeychainPayload:keychainData deviceID:deviceName ourPeerID:ourPeerID theirPeerID:theirPeerID operation:operationTypeAsString error:error];
136 }
137 else{ //message is less than the max encryption size, pass it along
138 secnotice("IDS Transport","sending message, no fragmentation: %@", data);
139 result = [self sendIDSMessage:data name:deviceName peer:ourPeerID identifier:&identifier error:error];
140 }
141 }
142 else
143 result = [self sendIDSMessage:data name:deviceName peer:ourPeerID identifier:&identifier error:error];
144
145
146
147 secnotice("IDS Transport","returning result: %d, error: %@", result, *error);
148 return result;
149 }
150
151 - (void)pingTimerFired:(NSString*)deviceID peerID:(NSString*)peerID identifier:(NSString*)identifier
152 {
153 secnotice("IDS Transport", "device ID: %@ !!!!!!!!!!!!!!!!Ping timeout is up!!!!!!!!!!!!", deviceID);
154 //call securityd to sync with device over KVS
155 __block CFErrorRef cf_error = NULL;
156 __block bool success = kHandleIDSMessageSuccess;
157
158 dispatch_source_t timer = [[IDSKeychainSyncingProxy idsProxy].pingTimers objectForKey:deviceID]; //remove timer
159 dispatch_cancel(timer); //cancel timer
160
161 [[IDSKeychainSyncingProxy idsProxy].pingTimers removeObjectForKey:deviceID];
162
163 [self sendKeysCallout:^NSMutableDictionary *(NSMutableDictionary *pending, NSError** error) {
164
165 success = SOSCCRequestSyncWithPeerOverKVS(((__bridge CFStringRef)deviceID), &cf_error);
166
167 if(success){
168 secnotice("IDSPing", "sent peerID: %@ to securityd to sync over KVS", deviceID);
169 }
170 else{
171 secerror("Could not hand peerID: %@ to securityd, error: %@", deviceID, cf_error);
172 }
173
174 return NULL;
175 }];
176 CFReleaseSafe(cf_error);
177 }
178
179
180 -(void) pingDevices:(NSArray*)list peerID:(NSString*)peerID
181 {
182 NSDictionary *messageDictionary = @{kIDSOperationType : [NSString stringWithFormat:@"%d", kIDSPeerAvailability], kIDSMessageToSendKey : @"checking peers"};
183
184 [list enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * top) {
185 NSString* deviceID = (NSString*)obj;
186 NSString* identifier = [NSString string];
187
188 secnotice("IDS Transport", "sending to id: %@", deviceID);
189 NSError *localErr = nil;
190
191 [self recordTimestampOfWriteToIDS: messageDictionary deviceName:deviceID peerID:peerID]; //add pings to throttling
192 NSDictionary *safeValues = [self filterForWritableValues:messageDictionary];
193
194 if(safeValues != nil && [safeValues count] > 0){
195 [self sendIDSMessage:safeValues name:deviceID peer:peerID identifier:&identifier error:&localErr];
196
197 if(localErr != nil){
198 secerror("sending ping to peer %@ had an error: %@", deviceID, localErr);
199 [self sendKeysCallout:^NSMutableDictionary *(NSMutableDictionary *pending, NSError** error) {
200 CFErrorRef kvsError = nil;
201 bool success = SOSCCRequestSyncWithPeerOverKVS(((__bridge CFStringRef)deviceID), &kvsError);
202
203 if(success){
204 secnotice("IDSPing", "sent peerID: %@ to securityd to sync over KVS", deviceID);
205 }
206 else{
207 secerror("Could not hand peerID: %@ to securityd, error: %@", deviceID, kvsError);
208 }
209 CFReleaseNull(kvsError);
210 return NULL;
211 }];
212 }
213 else{
214 dispatch_source_t timer = nil;
215 if( [self.pingTimers objectForKey:deviceID] == nil){
216 timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
217
218 dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, timeout * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, kRetryTimerLeeway);
219 dispatch_source_set_event_handler(timer, ^{
220 [self pingTimerFired:deviceID peerID:peerID identifier:identifier];
221 });
222 dispatch_resume(timer);
223
224 [self.pingTimers setObject:timer forKey:deviceID];
225 }
226 }
227 }
228 }];
229
230 }
231 -(BOOL) sendIDSMessage:(NSDictionary*)data name:(NSString*) deviceName peer:(NSString*) peerID identifier:(NSString **)identifier error:(NSError**) error
232 {
233 BOOL result = true;
234 NSDictionary *userInfo;
235 NSInteger code = 0;
236
237 NSString *errorMessage;
238 NSMutableSet *destinations = [NSMutableSet set];
239 NSArray *ListOfIDSDevices = nil;
240 IDSMessagePriority priority = IDSMessagePriorityHigh;
241 IDSDevice *device = nil;
242 BOOL encryptionOff = YES;
243 NSError *localError = nil;
244 NSString *sendersPeerIDKey = [ NSString stringWithUTF8String: kMessageKeySendersPeerID];
245
246 secnotice("backoff","!!writing these keys to IDS!!: %@", data);
247
248 NSDictionary *options = @{IDSSendMessageOptionForceEncryptionOffKey : [NSNumber numberWithBool:encryptionOff] };
249
250 NSMutableDictionary *dataCopy = [NSMutableDictionary dictionaryWithDictionary: data];
251
252 [dataCopy setObject:peerID forKey:sendersPeerIDKey];
253
254 secnotice("IDS Transport", "Sending message from: %@ to: %@", peerID, deviceName);
255
256 require_action_quiet(_service, fail, code = kSecIDSErrorNotRegistered; errorMessage = createErrorString(@"Could not send message to peer: %@: IDS delegate uninitialized, can't use IDS to send this message", deviceName));
257
258 secnotice("IDS Transport","devices: %@", [_service devices]);
259 secnotice("IDS Transport", " we have their deviceName: %@", deviceName);
260
261 ListOfIDSDevices = [_service devices];
262
263 require_action_quiet([ListOfIDSDevices count]> 0, fail, code = kSecIDSErrorNotRegistered; errorMessage=createErrorString(@"Could not send message to peer: %@: IDS devices are not registered yet", deviceName));
264 secnotice("IDS Transport","This is our list of devices: %@", ListOfIDSDevices);
265
266 for(NSUInteger i = 0; i < [ ListOfIDSDevices count ]; i++){
267 device = ListOfIDSDevices[i];
268 if( [ deviceName compare:device.uniqueID ] == 0){
269 [destinations addObject: IDSCopyIDForDevice(device)];
270 }
271 }
272
273 require_action_quiet([destinations count] != 0, fail, code = kSecIDSErrorCouldNotFindMatchingAuthToken; errorMessage = createErrorString(@"Could not send message to peer: %@: IDS device ID for peer does not match any devices within an IDS Account", deviceName));
274
275 result = [_service sendMessage:dataCopy toDestinations:destinations priority:priority options:options identifier:identifier error:&localError ] ;
276
277 require_action_quiet(localError == nil, fail, code = kSecIDSErrorFailedToSend; errorMessage = createErrorString(@"Had an error sending IDS message to peer: %@", deviceName));
278
279 secnotice("IDS Transport", "identifier: %@", *identifier);
280
281 secnotice("IDS Transport","sent to peer:%@, message: %@", deviceName, dataCopy);
282
283 return result;
284
285 fail:
286 userInfo = [ NSDictionary dictionaryWithObjectsAndKeys:errorMessage, NSLocalizedDescriptionKey, nil ];
287 if(error != nil){
288 *error = [NSError errorWithDomain:@"com.apple.security.ids.error" code:code userInfo:userInfo];
289 secerror("%@", *error);
290 }
291 if(localError != nil)
292 secerror("%@", localError);
293
294 return false;
295 }
296
297 - (void)service:(IDSService *)service account:(IDSAccount *)account identifier:(NSString *)identifier didSendWithSuccess:(BOOL)success error:(NSError *)error
298 {
299 if (error) {
300 NSLog(@"IDSKeychainSyncingProxy didSendWithSuccess identifier=%@ error=%@", identifier, error);
301 } else {
302 NSLog(@"IDSKeychainSyncingProxy didSendWithSuccess identifier=%@ Success!", identifier);
303 }
304 }
305
306 @end