]> git.saurik.com Git - apple/security.git/blob - KeychainSyncingOverIDSProxy/keychainsyncingoveridsproxy.m
Security-57740.51.3.tar.gz
[apple/security.git] / KeychainSyncingOverIDSProxy / keychainsyncingoveridsproxy.m
1 /*
2 * Copyright (c) 2012-2014 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 #include <AssertMacros.h>
25
26 #import <Foundation/Foundation.h>
27 #import <Security/Security.h>
28 #import <utilities/SecCFRelease.h>
29 #import <xpc/xpc.h>
30 #import <xpc/private.h>
31 #import <CoreFoundation/CFXPCBridge.h>
32 #import <sysexits.h>
33 #import <syslog.h>
34 #import <CommonCrypto/CommonDigest.h>
35 #include <utilities/SecXPCError.h>
36 #include <TargetConditionals.h>
37 #include "SOSCloudKeychainConstants.h"
38 #import <Security/SecureObjectSync/SOSInternal.h>
39
40 #import "KeychainSyncingOverIDSProxy+Throttle.h"
41 #import "KeychainSyncingOverIDSProxy+SendMessage.h"
42
43 int idsproxymain(int argc, const char *argv[]);
44
45 #define PROXYXPCSCOPE "idsproxy"
46
47 static void describeXPCObject(char *prefix, xpc_object_t object)
48 {
49 // This is useful for debugging.
50 if (object)
51 {
52 char *desc = xpc_copy_description(object);
53 secdebug(PROXYXPCSCOPE, "%s%s\n", prefix, desc);
54 free(desc);
55 }
56 else
57 secdebug(PROXYXPCSCOPE, "%s<NULL>\n", prefix);
58
59 }
60
61 static void idskeychainsyncingproxy_peer_dictionary_handler(const xpc_connection_t peer, xpc_object_t event)
62 {
63 bool result = false;
64 int err = 0;
65
66 require_action_string(xpc_get_type(event) == XPC_TYPE_DICTIONARY, xit, err = -51, "expected XPC_TYPE_DICTIONARY");
67
68 const char *operation = xpc_dictionary_get_string(event, kMessageKeyOperation);
69 require_action(operation, xit, result = false);
70
71 // Check protocol version
72 uint64_t version = xpc_dictionary_get_uint64(event, kMessageKeyVersion);
73 secdebug(PROXYXPCSCOPE, "Reply version: %lld\n", version);
74 require_action(version == kCKDXPCVersion, xit, result = false);
75
76 // Operations
77 secdebug(PROXYXPCSCOPE, "Handling %s operation", operation);
78
79
80 if(operation && !strcmp(operation, kOperationGetDeviceID)){
81 [[KeychainSyncingOverIDSProxy idsProxy] doSetIDSDeviceID];
82 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
83 xpc_dictionary_set_bool(replyMessage, kMessageKeyValue, true);
84 xpc_connection_send_message(peer, replyMessage);
85 secdebug(PROXYXPCSCOPE, "Set our IDS Device ID message sent");
86
87 }
88 else if (operation && !strcmp(operation, kOperationGetPendingMesages))
89 {
90 NSDictionary* messages = [[KeychainSyncingOverIDSProxy idsProxy] retrievePendingMessages];
91 xpc_object_t xMessages = _CFXPCCreateXPCObjectFromCFObject((__bridge CFDictionaryRef)(messages));
92
93 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
94 xpc_dictionary_set_value(replyMessage, kMessageKeyValue, xMessages);
95
96 xpc_connection_send_message(peer, replyMessage);
97 secdebug(PROXYXPCSCOPE, "retrieved pending messages");
98
99 }
100 else if(operation && !strcmp(operation, kOperationSendDeviceList)) //IDS device availability check
101 {
102 xpc_object_t xidsDeviceList = xpc_dictionary_get_value(event, kMessageKeyValue);
103 xpc_object_t xPeerID = xpc_dictionary_get_value(event, kMessageKeyPeerID);
104
105 NSArray *idsList = (__bridge_transfer NSArray*)(_CFXPCCreateCFObjectFromXPCObject(xidsDeviceList));
106 NSString *peerID = (__bridge_transfer NSString*)(_CFXPCCreateCFObjectFromXPCObject(xPeerID));
107
108 bool isMessageArray = (CFGetTypeID((__bridge CFTypeRef)(idsList)) == CFArrayGetTypeID());
109 bool isPeerIDString = (CFGetTypeID((__bridge CFTypeRef)(peerID)) == CFStringGetTypeID());
110
111 require_quiet(isMessageArray, xit);
112 require_quiet(isPeerIDString, xit);
113
114 [[KeychainSyncingOverIDSProxy idsProxy] pingDevices:idsList peerID:peerID];
115
116 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
117 xpc_dictionary_set_bool(replyMessage, kMessageKeyValue, true);
118
119 xpc_connection_send_message(peer, replyMessage);
120 secdebug(PROXYXPCSCOPE, "IDS device list sent");
121 }
122 else if (operation && !strcmp(operation, kOperationSendFragmentedIDSMessage))
123 {
124 xpc_object_t xidsMessageData = xpc_dictionary_get_value(event, kMessageKeyValue);
125 xpc_object_t xDeviceName = xpc_dictionary_get_value(event, kMessageKeyDeviceName);
126 xpc_object_t xPeerID = xpc_dictionary_get_value(event, kMessageKeyPeerID);
127 BOOL object = false;
128
129 NSString *deviceName = (__bridge_transfer NSString*)(_CFXPCCreateCFObjectFromXPCObject(xDeviceName));
130 NSString *peerID = (__bridge_transfer NSString*)(_CFXPCCreateCFObjectFromXPCObject(xPeerID));
131 NSDictionary *messageDictionary = (__bridge_transfer NSDictionary*)(_CFXPCCreateCFObjectFromXPCObject(xidsMessageData));
132 NSError *error = NULL;
133 bool isNameString = (CFGetTypeID((__bridge CFTypeRef)(deviceName)) == CFStringGetTypeID());
134 bool isPeerIDString = (CFGetTypeID((__bridge CFTypeRef)(peerID)) == CFStringGetTypeID());
135 bool isMessageDictionary = (CFGetTypeID((__bridge CFTypeRef)(messageDictionary)) == CFDictionaryGetTypeID());
136
137 require_quiet(isNameString, xit);
138 require_quiet(isPeerIDString, xit);
139 require_quiet(isMessageDictionary, xit);
140
141 [[KeychainSyncingOverIDSProxy idsProxy] recordTimestampOfWriteToIDS: messageDictionary deviceName:deviceName peerID:peerID];
142 NSDictionary *safeValues = [[KeychainSyncingOverIDSProxy idsProxy] filterForWritableValues:messageDictionary];
143
144 if(safeValues != nil && [safeValues count] > 0){
145 object = [[KeychainSyncingOverIDSProxy idsProxy] sendFragmentedIDSMessages:safeValues name:deviceName peer:peerID error:&error];
146 }
147
148 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
149 xpc_dictionary_set_bool(replyMessage, kMessageKeyValue, object);
150
151 if(error){
152 xpc_object_t xerrobj = SecCreateXPCObjectWithCFError((__bridge CFErrorRef)(error));
153 xpc_dictionary_set_value(replyMessage, kMessageKeyError, xerrobj);
154 }
155 xpc_connection_send_message(peer, replyMessage);
156 secdebug(PROXYXPCSCOPE, "IDS message sent");
157 }
158 else if (operation && !strcmp(operation, kOperationSendIDSMessage)) //for IDS tests
159 {
160 xpc_object_t xidsMessageData = xpc_dictionary_get_value(event, kMessageKeyValue);
161 xpc_object_t xDeviceName = xpc_dictionary_get_value(event, kMessageKeyDeviceName);
162 xpc_object_t xPeerID = xpc_dictionary_get_value(event, kMessageKeyPeerID);
163 BOOL object = false;
164
165 NSString *deviceName = (__bridge_transfer NSString*)(_CFXPCCreateCFObjectFromXPCObject(xDeviceName));
166 NSString *peerID = (__bridge_transfer NSString*)(_CFXPCCreateCFObjectFromXPCObject(xPeerID));
167 NSDictionary *messageDictionary = (__bridge_transfer NSDictionary*)(_CFXPCCreateCFObjectFromXPCObject(xidsMessageData));
168 NSError *error = NULL;
169 bool isNameString = (CFGetTypeID((__bridge CFTypeRef)(deviceName)) == CFStringGetTypeID());
170 bool isPeerIDString = (CFGetTypeID((__bridge CFTypeRef)(peerID)) == CFStringGetTypeID());
171 bool isMessageDictionary = (CFGetTypeID((__bridge CFTypeRef)(messageDictionary)) == CFDictionaryGetTypeID());
172
173 require_quiet(isNameString, xit);
174 require_quiet(isPeerIDString, xit);
175 require_quiet(isMessageDictionary, xit);
176
177 [[KeychainSyncingOverIDSProxy idsProxy] recordTimestampOfWriteToIDS: messageDictionary deviceName:deviceName peerID:peerID];
178 NSDictionary *safeValues = [[KeychainSyncingOverIDSProxy idsProxy] filterForWritableValues:messageDictionary];
179
180 if(safeValues != nil && [safeValues count] > 0){
181 NSString *localMessageIdentifier = [[NSUUID UUID] UUIDString];
182 NSMutableDictionary* safeValuesCopy = [NSMutableDictionary dictionaryWithDictionary:safeValues];
183
184 [safeValuesCopy setObject:localMessageIdentifier forKey:(__bridge NSString*)(kIDSMessageUniqueID)];
185
186 if([[KeychainSyncingOverIDSProxy idsProxy] sendIDSMessage:safeValuesCopy name:deviceName peer:peerID])
187 {
188 object = true;
189 NSString *useAckModel = [safeValuesCopy objectForKey:(__bridge NSString*)(kIDSMessageUsesAckModel)];
190 if(object && [useAckModel compare:@"YES"] == NSOrderedSame){
191 secnotice("IDS Transport", "setting timer!");
192 [[KeychainSyncingOverIDSProxy idsProxy] setMessageTimer:localMessageIdentifier deviceID:deviceName message:safeValuesCopy];
193 }
194 }
195 else{
196 secerror("Could not send message");
197 }
198
199 }
200
201 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
202 xpc_dictionary_set_bool(replyMessage, kMessageKeyValue, object);
203
204 if(error){
205 xpc_object_t xerrobj = SecCreateXPCObjectWithCFError((__bridge CFErrorRef)(error));
206 xpc_dictionary_set_value(replyMessage, kMessageKeyError, xerrobj);
207 }
208 xpc_connection_send_message(peer, replyMessage);
209 secdebug(PROXYXPCSCOPE, "IDS message sent");
210 }
211
212 else
213 {
214 char *description = xpc_copy_description(event);
215 secdebug(PROXYXPCSCOPE, "Unknown op=%s request from pid %d: %s", operation, xpc_connection_get_pid(peer), description);
216 free(description);
217 }
218 result = true;
219 xit:
220 if (!result)
221 describeXPCObject("handle_operation fail: ", event);
222 }
223
224 static void idskeychainsyncingproxy_peer_event_handler(xpc_connection_t peer, xpc_object_t event)
225 {
226 describeXPCObject("peer: ", peer);
227 xpc_type_t type = xpc_get_type(event);
228 if (type == XPC_TYPE_ERROR) {
229 if (event == XPC_ERROR_CONNECTION_INVALID) {
230 // The client process on the other end of the connection has either
231 // crashed or cancelled the connection. After receiving this error,
232 // the connection is in an invalid state, and you do not need to
233 // call xpc_connection_cancel(). Just tear down any associated state
234 // here.
235 } else if (event == XPC_ERROR_TERMINATION_IMMINENT) {
236 // Handle per-connection termination cleanup.
237 }
238 } else {
239 assert(type == XPC_TYPE_DICTIONARY);
240 // Handle the message.
241 // describeXPCObject("dictionary:", event);
242 dispatch_async(dispatch_get_main_queue(), ^{
243 idskeychainsyncingproxy_peer_dictionary_handler(peer, event);
244 });
245 }
246 }
247
248 static void idskeychainsyncingproxy_event_handler(xpc_connection_t peer)
249 {
250 // By defaults, new connections will target the default dispatch
251 // concurrent queue.
252
253 if (xpc_get_type(peer) != XPC_TYPE_CONNECTION)
254 {
255 secdebug(PROXYXPCSCOPE, "expected XPC_TYPE_CONNECTION");
256 return;
257 }
258
259 xpc_connection_set_event_handler(peer, ^(xpc_object_t event)
260 {
261 idskeychainsyncingproxy_peer_event_handler(peer, event);
262 });
263
264 // This will tell the connection to begin listening for events. If you
265 // have some other initialization that must be done asynchronously, then
266 // you can defer this call until after that initialization is done.
267 xpc_connection_resume(peer);
268 }
269
270 int idsproxymain(int argc, const char *argv[])
271 {
272 secdebug(PROXYXPCSCOPE, "Starting IDSProxy");
273 char *wait4debugger = getenv("WAIT4DEBUGGER");
274
275 if (wait4debugger && !strcasecmp("YES", wait4debugger))
276 {
277 syslog(LOG_ERR, "Waiting for debugger");
278 kill(getpid(), SIGTSTP);
279 }
280
281 // DISPATCH_TARGET_QUEUE_DEFAULT
282 xpc_connection_t listener = xpc_connection_create_mach_service(xpcIDSServiceName, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
283 xpc_connection_set_event_handler(listener, ^(xpc_object_t object){ idskeychainsyncingproxy_event_handler(object); });
284
285 [KeychainSyncingOverIDSProxy idsProxy];
286
287 if([[KeychainSyncingOverIDSProxy idsProxy].messagesInFlight count] > 0 &&
288 [KeychainSyncingOverIDSProxy idsProxy].isIDSInitDone &&
289 [KeychainSyncingOverIDSProxy idsProxy].sendRestoredMessages){
290 [[KeychainSyncingOverIDSProxy idsProxy] sendPersistedMessagesAgain];
291 [KeychainSyncingOverIDSProxy idsProxy].sendRestoredMessages = false;
292 }
293
294 // It looks to me like there is insufficient locking to allow a request to come in on the XPC connection while doing the initial all items.
295 // Therefore I'm leaving the XPC connection suspended until that has time to process.
296 xpc_connection_resume(listener);
297
298 @autoreleasepool
299 {
300 secdebug(PROXYXPCSCOPE, "Starting mainRunLoop");
301 NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
302 [runLoop run];
303 }
304
305 secdebug(PROXYXPCSCOPE, "Exiting KeychainSyncingOverIDSProxy");
306
307 return EXIT_FAILURE;
308 }
309
310 int main(int argc, const char *argv[])
311 {
312 return idsproxymain(argc, argv);
313 }