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