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