2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include <AssertMacros.h>
26 #import <Foundation/Foundation.h>
27 #import <Security/Security.h>
28 #import <utilities/SecCFRelease.h>
30 #import <xpc/private.h>
31 #import <CoreFoundation/CFXPCBridge.h>
34 #import <CommonCrypto/CommonDigest.h>
35 #include <utilities/SecXPCError.h>
36 #include <TargetConditionals.h>
37 #include "SOSCloudKeychainConstants.h"
40 #import "IDSKeychainSyncingProxy+IDSProxyThrottle.h"
41 #import "IDSKeychainSyncingProxy+IDSProxySendMessage.h"
43 int idsproxymain(int argc, const char *argv[]);
45 #define PROXYXPCSCOPE "idsproxy"
47 static void describeXPCObject(char *prefix, xpc_object_t object)
49 // This is useful for debugging.
52 char *desc = xpc_copy_description(object);
53 secdebug(PROXYXPCSCOPE, "%s%s\n", prefix, desc);
57 secdebug(PROXYXPCSCOPE, "%s<NULL>\n", prefix);
61 static void idskeychainsyncingproxy_peer_dictionary_handler(const xpc_connection_t peer, xpc_object_t event)
66 require_action_string(xpc_get_type(event) == XPC_TYPE_DICTIONARY, xit, err = -51, "expected XPC_TYPE_DICTIONARY");
68 const char *operation = xpc_dictionary_get_string(event, kMessageKeyOperation);
69 require_action(operation, xit, result = false);
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);
77 secdebug(PROXYXPCSCOPE, "Handling %s operation", operation);
80 if(operation && !strcmp(operation, kOperationGetDeviceID)){
81 [[IDSKeychainSyncingProxy 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");
88 else if (operation && !strcmp(operation, kOperationSendFragmentedIDSMessage))
90 xpc_object_t xidsMessageData = xpc_dictionary_get_value(event, kMessageKeyValue);
91 xpc_object_t xDeviceName = xpc_dictionary_get_value(event, kMessageKeyDeviceName);
92 xpc_object_t xPeerID = xpc_dictionary_get_value(event, kMessageKeyPeerID);
95 NSString *deviceName = (__bridge_transfer NSString*)(_CFXPCCreateCFObjectFromXPCObject(xDeviceName));
96 NSString *peerID = (__bridge_transfer NSString*)(_CFXPCCreateCFObjectFromXPCObject(xPeerID));
97 NSDictionary *messageDictionary = (__bridge_transfer NSDictionary*)(_CFXPCCreateCFObjectFromXPCObject(xidsMessageData));
98 NSError *error = NULL;
99 bool isNameString = (CFGetTypeID((__bridge CFTypeRef)(deviceName)) == CFStringGetTypeID());
100 bool isPeerIDString = (CFGetTypeID((__bridge CFTypeRef)(peerID)) == CFStringGetTypeID());
101 bool isMessageDictionary = (CFGetTypeID((__bridge CFTypeRef)(messageDictionary)) == CFDictionaryGetTypeID());
103 require_quiet(isNameString, xit);
104 require_quiet(isPeerIDString, xit);
105 require_quiet(isMessageDictionary, xit);
107 [[IDSKeychainSyncingProxy idsProxy] recordTimestampOfWriteToIDS: messageDictionary deviceName:deviceName peerID:peerID];
108 NSDictionary *safeValues = [[IDSKeychainSyncingProxy idsProxy] filterForWritableValues:messageDictionary];
110 if(safeValues != nil && [safeValues count] > 0){
111 object = [[IDSKeychainSyncingProxy idsProxy] sendFragmentedIDSMessages:safeValues name:deviceName peer:peerID error:&error];
114 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
115 xpc_dictionary_set_bool(replyMessage, kMessageKeyValue, object);
118 xpc_object_t xerrobj = SecCreateXPCObjectWithCFError((__bridge CFErrorRef)(error));
119 xpc_dictionary_set_value(replyMessage, kMessageKeyError, xerrobj);
121 xpc_connection_send_message(peer, replyMessage);
122 secdebug(PROXYXPCSCOPE, "IDS message sent");
124 else if(operation && !strcmp(operation, kOperationSendDeviceList)) //IDS device availability check
126 xpc_object_t xidsDeviceList = xpc_dictionary_get_value(event, kMessageKeyValue);
127 xpc_object_t xPeerID = xpc_dictionary_get_value(event, kMessageKeyPeerID);
129 NSArray *idsList = (__bridge_transfer NSArray*)(_CFXPCCreateCFObjectFromXPCObject(xidsDeviceList));
130 NSString *peerID = (__bridge_transfer NSString*)(_CFXPCCreateCFObjectFromXPCObject(xPeerID));
132 bool isMessageArray = (CFGetTypeID((__bridge CFTypeRef)(idsList)) == CFArrayGetTypeID());
133 bool isPeerIDString = (CFGetTypeID((__bridge CFTypeRef)(peerID)) == CFStringGetTypeID());
135 require_quiet(isMessageArray, xit);
136 require_quiet(isPeerIDString, xit);
138 [[IDSKeychainSyncingProxy idsProxy] pingDevices:idsList peerID:peerID];
140 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
141 xpc_dictionary_set_bool(replyMessage, kMessageKeyValue, true);
143 xpc_connection_send_message(peer, replyMessage);
144 secdebug(PROXYXPCSCOPE, "IDS device list sent");
146 else if (operation && !strcmp(operation, kOperationSendIDSMessage)) //for IDS tests
148 xpc_object_t xidsMessageData = xpc_dictionary_get_value(event, kMessageKeyValue);
149 xpc_object_t xDeviceName = xpc_dictionary_get_value(event, kMessageKeyDeviceName);
150 xpc_object_t xPeerID = xpc_dictionary_get_value(event, kMessageKeyPeerID);
153 NSString *deviceName = (__bridge_transfer NSString*)(_CFXPCCreateCFObjectFromXPCObject(xDeviceName));
154 NSString *peerID = (__bridge_transfer NSString*)(_CFXPCCreateCFObjectFromXPCObject(xPeerID));
155 NSDictionary *messageDictionary = (__bridge_transfer NSDictionary*)(_CFXPCCreateCFObjectFromXPCObject(xidsMessageData));
156 NSError *error = NULL;
157 bool isNameString = (CFGetTypeID((__bridge CFTypeRef)(deviceName)) == CFStringGetTypeID());
158 bool isPeerIDString = (CFGetTypeID((__bridge CFTypeRef)(peerID)) == CFStringGetTypeID());
159 bool isMessageDictionary = (CFGetTypeID((__bridge CFTypeRef)(messageDictionary)) == CFDictionaryGetTypeID());
161 require_quiet(isNameString, xit);
162 require_quiet(isPeerIDString, xit);
163 require_quiet(isMessageDictionary, xit);
165 NSString *identifier = [NSString string];
166 object = [[IDSKeychainSyncingProxy idsProxy] sendIDSMessage:messageDictionary name:deviceName peer:peerID identifier:&identifier error:&error];
168 xpc_object_t replyMessage = xpc_dictionary_create_reply(event);
169 xpc_dictionary_set_bool(replyMessage, kMessageKeyValue, object);
172 xpc_object_t xerrobj = SecCreateXPCObjectWithCFError((__bridge CFErrorRef)(error));
173 xpc_dictionary_set_value(replyMessage, kMessageKeyError, xerrobj);
175 xpc_connection_send_message(peer, replyMessage);
176 secdebug(PROXYXPCSCOPE, "IDS message sent");
181 char *description = xpc_copy_description(event);
182 secdebug(PROXYXPCSCOPE, "Unknown op=%s request from pid %d: %s", operation, xpc_connection_get_pid(peer), description);
188 describeXPCObject("handle_operation fail: ", event);
191 static void idskeychainsyncingproxy_peer_event_handler(xpc_connection_t peer, xpc_object_t event)
193 describeXPCObject("peer: ", peer);
194 xpc_type_t type = xpc_get_type(event);
195 if (type == XPC_TYPE_ERROR) {
196 if (event == XPC_ERROR_CONNECTION_INVALID) {
197 // The client process on the other end of the connection has either
198 // crashed or cancelled the connection. After receiving this error,
199 // the connection is in an invalid state, and you do not need to
200 // call xpc_connection_cancel(). Just tear down any associated state
202 } else if (event == XPC_ERROR_TERMINATION_IMMINENT) {
203 // Handle per-connection termination cleanup.
206 assert(type == XPC_TYPE_DICTIONARY);
207 // Handle the message.
208 // describeXPCObject("dictionary:", event);
209 dispatch_async(dispatch_get_main_queue(), ^{
210 idskeychainsyncingproxy_peer_dictionary_handler(peer, event);
215 static void idskeychainsyncingproxy_event_handler(xpc_connection_t peer)
217 // By defaults, new connections will target the default dispatch
220 if (xpc_get_type(peer) != XPC_TYPE_CONNECTION)
222 secdebug(PROXYXPCSCOPE, "expected XPC_TYPE_CONNECTION");
226 xpc_connection_set_event_handler(peer, ^(xpc_object_t event)
228 idskeychainsyncingproxy_peer_event_handler(peer, event);
231 // This will tell the connection to begin listening for events. If you
232 // have some other initialization that must be done asynchronously, then
233 // you can defer this call until after that initialization is done.
234 xpc_connection_resume(peer);
237 int idsproxymain(int argc, const char *argv[])
239 secdebug(PROXYXPCSCOPE, "Starting IDSProxy");
240 char *wait4debugger = getenv("WAIT4DEBUGGER");
242 if (wait4debugger && !strcasecmp("YES", wait4debugger))
244 syslog(LOG_ERR, "Waiting for debugger");
245 kill(getpid(), SIGTSTP);
248 // DISPATCH_TARGET_QUEUE_DEFAULT
249 xpc_connection_t listener = xpc_connection_create_mach_service(xpcIDSServiceName, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);
250 xpc_connection_set_event_handler(listener, ^(xpc_object_t object){ idskeychainsyncingproxy_event_handler(object); });
252 [IDSKeychainSyncingProxy idsProxy];
254 // 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.
255 // Therefore I'm leaving the XPC connection suspended until that has time to process.
256 xpc_connection_resume(listener);
260 secdebug(PROXYXPCSCOPE, "Starting mainRunLoop");
261 NSRunLoop *runLoop = [NSRunLoop mainRunLoop];
265 secdebug(PROXYXPCSCOPE, "Exiting IDSKeychainSyncingProxy");
270 int main(int argc, const char *argv[])
272 return idsproxymain(argc, argv);