]> git.saurik.com Git - apple/security.git/blob - OSX/sec/ipc/client.c
790d59d775b40c2e64b24dbf6a4097f316a63c9a
[apple/security.git] / OSX / sec / ipc / client.c
1 /*
2 * Copyright (c) 2007-2009,2012-2015 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 <stdbool.h>
25 #include <sys/queue.h>
26 #include <syslog.h>
27 #include <vproc_priv.h>
28
29 #include <CoreFoundation/CoreFoundation.h>
30 #include <Security/SecItem.h>
31 #include <Security/SecBasePriv.h>
32 #include <Security/SecInternal.h>
33 #include <Security/SecuritydXPC.h>
34 #include <Security/SecTask.h>
35 #include <Security/SecItemPriv.h>
36
37 #include <utilities/debugging.h>
38 #include <utilities/SecCFError.h>
39 #include <utilities/SecXPCError.h>
40 #include <utilities/SecCFWrappers.h>
41 #include <utilities/SecDispatchRelease.h>
42 #include <utilities/SecDb.h> // TODO Fixme this gets us SecError().
43 #include <ipc/securityd_client.h>
44
45 struct securityd *gSecurityd;
46
47 //
48 // MARK: XPC IPC.
49 //
50
51 /* Hardcoded Access Groups for the server itself */
52 static CFArrayRef SecServerCopyAccessGroups(void) {
53 return CFArrayCreateForCFTypes(kCFAllocatorDefault,
54 #if NO_SERVER
55 CFSTR("test"),
56 CFSTR("apple"),
57 CFSTR("lockdown-identities"),
58 #else
59 CFSTR("sync"),
60 #endif
61 CFSTR("com.apple.security.sos"),
62 CFSTR("com.apple.sbd"),
63 CFSTR("com.apple.lakitu"),
64 NULL);
65 }
66
67 static SecurityClient gClient;
68
69 #if TARGET_OS_IOS
70 void
71 SecSecuritySetMusrMode(bool mode, uid_t uid, int activeUser)
72 {
73 gClient.inMultiUser = mode;
74 gClient.uid = uid;
75 gClient.activeUser = activeUser;
76 }
77 #endif
78
79 SecurityClient *
80 SecSecurityClientGet(void)
81 {
82 static dispatch_once_t onceToken;
83 dispatch_once(&onceToken, ^{
84 gClient.task = NULL,
85 gClient.accessGroups = SecServerCopyAccessGroups();
86 gClient.allowSystemKeychain = true;
87 gClient.allowSyncBubbleKeychain = true;
88 gClient.isNetworkExtension = false;
89 #if TARGET_OS_IPHONE
90 gClient.inMultiUser = false;
91 gClient.activeUser = 501;
92 #endif
93 });
94 return &gClient;
95 }
96
97 CFArrayRef SecAccessGroupsGetCurrent(void) {
98 SecurityClient *client = SecSecurityClientGet();
99 assert(client && client->accessGroups);
100 return client->accessGroups;
101 }
102
103 // Only for testing.
104 void SecAccessGroupsSetCurrent(CFArrayRef accessGroups);
105 void SecAccessGroupsSetCurrent(CFArrayRef accessGroups) {
106 // Not thread safe at all, but OK because it is meant to be used only by tests.
107 gClient.accessGroups = accessGroups;
108 }
109
110 #if !TARGET_OS_IPHONE
111 static bool securityd_in_system_context(void) {
112 static bool runningInSystemContext;
113 static dispatch_once_t onceToken;
114 dispatch_once(&onceToken, ^{
115 runningInSystemContext = (getuid() == 0);
116 if (!runningInSystemContext) {
117 char *manager;
118 if (vproc_swap_string(NULL, VPROC_GSK_MGR_NAME, NULL, &manager) == NULL) {
119 runningInSystemContext = (!strcmp(manager, VPROCMGR_SESSION_SYSTEM) ||
120 !strcmp(manager, VPROCMGR_SESSION_LOGINWINDOW));
121 free(manager);
122 }
123 }
124 });
125 return runningInSystemContext;
126 }
127 #endif
128
129 static const char *securityd_service_name(void) {
130 return kSecuritydXPCServiceName;
131 }
132
133 static xpc_connection_t securityd_create_connection(const char *name, uint64_t flags) {
134 const char *serviceName = name;
135 if (!serviceName) {
136 serviceName = securityd_service_name();
137 }
138 xpc_connection_t connection;
139 connection = xpc_connection_create_mach_service(serviceName, NULL, flags);
140 xpc_connection_set_event_handler(connection, ^(xpc_object_t event) {
141 const char *description = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION);
142 secnotice("xpc", "got event: %s", description);
143 });
144 xpc_connection_resume(connection);
145 return connection;
146 }
147
148 static xpc_connection_t sSecuritydConnection;
149 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
150 static xpc_connection_t sTrustdConnection;
151 #endif
152
153 static xpc_connection_t securityd_connection(void) {
154 static dispatch_once_t once;
155 dispatch_once(&once, ^{
156 sSecuritydConnection = securityd_create_connection(kSecuritydXPCServiceName, 0);
157 });
158 return sSecuritydConnection;
159 }
160
161 static xpc_connection_t trustd_connection(void) {
162 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
163 static dispatch_once_t once;
164 dispatch_once(&once, ^{
165 bool sysCtx = securityd_in_system_context();
166 uint64_t flags = (sysCtx) ? XPC_CONNECTION_MACH_SERVICE_PRIVILEGED : 0;
167 const char *serviceName = (sysCtx) ? kTrustdXPCServiceName : kTrustdAgentXPCServiceName;
168 sTrustdConnection = securityd_create_connection(serviceName, flags);
169 });
170 return sTrustdConnection;
171 #else
172 // on iOS all operations are still handled by securityd
173 return securityd_connection();
174 #endif
175 }
176
177 static xpc_connection_t securityd_connection_for_operation(enum SecXPCOperation op) {
178 bool isTrustOp;
179 switch (op) {
180 case sec_trust_store_contains_id:
181 case sec_trust_store_set_trust_settings_id:
182 case sec_trust_store_remove_certificate_id:
183 case sec_trust_evaluate_id:
184 isTrustOp = true;
185 break;
186 default:
187 isTrustOp = false;
188 break;
189 }
190 #if SECTRUST_VERBOSE_DEBUG
191 {
192 bool sysCtx = securityd_in_system_context();
193 const char *serviceName = (sysCtx) ? kTrustdXPCServiceName : kTrustdAgentXPCServiceName;
194 syslog(LOG_ERR, "Will connect to: %s (op=%d)",
195 (isTrustOp) ? serviceName : kSecuritydXPCServiceName, (int)op);
196 }
197 #endif
198 return (isTrustOp) ? trustd_connection() : securityd_connection();
199 }
200
201 // NOTE: This is not thread safe, but this SPI is for testing only.
202 void SecServerSetMachServiceName(const char *name) {
203 // Make sure sSecXPCServer.queue exists.
204 securityd_connection();
205
206 xpc_connection_t oldConection = sSecuritydConnection;
207 sSecuritydConnection = securityd_create_connection(name, 0);
208 if (oldConection)
209 xpc_release(oldConection);
210 }
211
212 xpc_object_t
213 securityd_message_with_reply_sync(xpc_object_t message, CFErrorRef *error)
214 {
215 xpc_object_t reply = NULL;
216 uint64_t operation = xpc_dictionary_get_uint64(message, kSecXPCKeyOperation);
217 xpc_connection_t connection = securityd_connection_for_operation((enum SecXPCOperation)operation);
218
219 const int max_tries = 4; // Per <rdar://problem/17829836> N61/12A342: Audio Playback... for why this needs to be at least 3, so we made it 4.
220
221 unsigned int tries_left = max_tries;
222 do {
223 if (reply) xpc_release(reply);
224 reply = xpc_connection_send_message_with_reply_sync(connection, message);
225 } while (reply == XPC_ERROR_CONNECTION_INTERRUPTED && --tries_left > 0);
226
227 if (xpc_get_type(reply) == XPC_TYPE_ERROR) {
228 CFIndex code = 0;
229 if (reply == XPC_ERROR_CONNECTION_INTERRUPTED || reply == XPC_ERROR_CONNECTION_INVALID) {
230 code = kSecXPCErrorConnectionFailed;
231 seccritical("Failed to talk to secd after %d attempts.", max_tries);
232 } else if (reply == XPC_ERROR_TERMINATION_IMMINENT)
233 code = kSecXPCErrorUnknown;
234 else
235 code = kSecXPCErrorUnknown;
236
237 char *conn_desc = xpc_copy_description(connection);
238 const char *description = xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION);
239 SecCFCreateErrorWithFormat(code, sSecXPCErrorDomain, NULL, error, NULL, CFSTR("%s: %s"), conn_desc, description);
240 free(conn_desc);
241 xpc_release(reply);
242 reply = NULL;
243 }
244
245 return reply;
246 }
247
248 xpc_object_t securityd_create_message(enum SecXPCOperation op, CFErrorRef* error)
249 {
250 xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
251 if (message) {
252 xpc_dictionary_set_uint64(message, kSecXPCKeyOperation, op);
253 } else {
254 SecCFCreateError(kSecXPCErrorConnectionFailed, sSecXPCErrorDomain,
255 CFSTR("xpc_dictionary_create returned NULL"), NULL, error);
256 }
257 return message;
258 }
259
260 // Return true if there is no error in message, return false and set *error if there is.
261 bool securityd_message_no_error(xpc_object_t message, CFErrorRef *error) {
262 xpc_object_t xpc_error = xpc_dictionary_get_value(message, kSecXPCKeyError);
263 if (xpc_error == NULL)
264 return true;
265
266 if (error) {
267 *error = SecCreateCFErrorWithXPCObject(xpc_error);
268 }
269 return false;
270 }
271
272 bool securityd_send_sync_and_do(enum SecXPCOperation op, CFErrorRef *error,
273 bool (^add_to_message)(xpc_object_t message, CFErrorRef* error),
274 bool (^handle_response)(xpc_object_t response, CFErrorRef* error)) {
275 xpc_object_t message = securityd_create_message(op, error);
276 bool ok = false;
277 if (message) {
278 if (!add_to_message || add_to_message(message, error)) {
279 xpc_object_t response = securityd_message_with_reply_sync(message, error);
280 if (response) {
281 if (securityd_message_no_error(response, error)) {
282 ok = (!handle_response || handle_response(response, error));
283 }
284 xpc_release(response);
285 }
286 }
287 xpc_release(message);
288 }
289
290 return ok;
291 }
292
293
294 CFDictionaryRef
295 _SecSecuritydCopyWhoAmI(CFErrorRef *error)
296 {
297 CFDictionaryRef reply = NULL;
298 xpc_object_t message = securityd_create_message(kSecXPCOpWhoAmI, error);
299 if (message) {
300 xpc_object_t response = securityd_message_with_reply_sync(message, error);
301 if (response) {
302 reply = _CFXPCCreateCFObjectFromXPCObject(response);
303 xpc_release(response);
304 } else {
305 securityd_message_no_error(response, error);
306 }
307 xpc_release(message);
308 }
309 return reply;
310 }
311
312 bool
313 _SecSyncBubbleTransfer(CFArrayRef services, uid_t uid, CFErrorRef *error)
314 {
315 xpc_object_t message;
316 bool reply = false;
317
318 message = securityd_create_message(kSecXPCOpTransmogrifyToSyncBubble, error);
319 if (message) {
320 xpc_dictionary_set_int64(message, "uid", uid);
321 if (SecXPCDictionarySetPList(message, "services", services, error)) {
322 xpc_object_t response = securityd_message_with_reply_sync(message, error);
323 if (response) {
324 reply = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
325 if (!reply)
326 securityd_message_no_error(response, error);
327 xpc_release(response);
328 }
329 xpc_release(message);
330 }
331 }
332 return reply;
333 }
334
335 bool
336 _SecSystemKeychainTransfer(CFErrorRef *error)
337 {
338 xpc_object_t message;
339 bool reply = false;
340
341 message = securityd_create_message(kSecXPCOpTransmogrifyToSystemKeychain, error);
342 if (message) {
343 xpc_object_t response = securityd_message_with_reply_sync(message, error);
344 if (response) {
345 reply = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
346 if (!reply)
347 securityd_message_no_error(response, error);
348 xpc_release(response);
349 }
350 xpc_release(message);
351 }
352 return reply;
353 }
354
355 bool
356 _SecSyncDeleteUserViews(uid_t uid, CFErrorRef *error)
357 {
358 xpc_object_t message;
359 bool reply = false;
360
361 message = securityd_create_message(kSecXPCOpDeleteUserView, error);
362 if (message) {
363 xpc_dictionary_set_int64(message, "uid", uid);
364
365 xpc_object_t response = securityd_message_with_reply_sync(message, error);
366 if (response) {
367 reply = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
368 if (!reply)
369 securityd_message_no_error(response, error);
370 xpc_release(response);
371 }
372 xpc_release(message);
373 }
374 return reply;
375 }
376
377
378
379 /* vi:set ts=4 sw=4 et: */