2 * Copyright (c) 2007-2009,2012-2015 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 <TargetConditionals.h>
26 // client.c is from the iOS world and must compile with an iOS view of the headers
27 #ifndef SEC_IOS_ON_OSX
28 #define SEC_IOS_ON_OSX 1
29 #endif // SEC_IOS_ON_OSX
32 #ifndef SECITEM_SHIM_OSX
33 #define SECITEM_SHIM_OSX 1
34 #endif // SECITEM_SHIM_OSX
35 #endif // TARGET_OS_OSX
38 #include <sys/queue.h>
40 #include <vproc_priv.h>
42 #include <xpc/private.h>
44 #include <CoreFoundation/CoreFoundation.h>
45 #include <Security/SecItem.h>
46 #include <Security/SecBasePriv.h>
47 #include <Security/SecInternal.h>
48 #include <Security/SecuritydXPC.h>
49 #include <Security/SecTask.h>
50 #include <Security/SecItemPriv.h>
52 #include <utilities/debugging.h>
53 #include <utilities/SecCFError.h>
54 #include <utilities/SecXPCError.h>
55 #include <utilities/SecCFWrappers.h>
56 #include <utilities/SecDispatchRelease.h>
57 #include <utilities/SecDb.h> // TODO Fixme this gets us SecError().
58 #include <utilities/SecAKSWrappers.h>
59 #include <ipc/securityd_client.h>
61 #include "server_security_helpers.h"
63 struct securityd
*gSecurityd
;
64 struct trustd
*gTrustd
;
70 /* Hardcoded Access Groups for the server itself */
71 static CFArrayRef
SecServerCopyAccessGroups(void) {
72 return CFArrayCreateForCFTypes(kCFAllocatorDefault
,
76 CFSTR("lockdown-identities"),
77 CFSTR("123456.test.group"),
78 CFSTR("123456.test.group2"),
79 CFSTR("com.apple.cfnetwork"),
83 CFSTR("com.apple.security.sos"),
84 CFSTR("com.apple.security.ckks"),
85 CFSTR("com.apple.security.sos-usercredential"),
86 CFSTR("com.apple.sbd"),
87 CFSTR("com.apple.lakitu"),
88 CFSTR("com.apple.security.securityd"),
89 kSecAttrAccessGroupToken
,
93 static SecurityClient gClient
;
97 SecSecuritySetMusrMode(bool mode
, uid_t uid
, int activeUser
)
99 gClient
.inMultiUser
= mode
;
101 gClient
.activeUser
= activeUser
;
106 SecSecurityClientGet(void)
108 static dispatch_once_t onceToken
;
109 dispatch_once(&onceToken
, ^{
111 gClient
.accessGroups
= SecServerCopyAccessGroups();
112 gClient
.allowSystemKeychain
= true;
113 gClient
.allowSyncBubbleKeychain
= true;
114 gClient
.isNetworkExtension
= false;
116 gClient
.inMultiUser
= false;
117 gClient
.activeUser
= 501;
123 CFArrayRef
SecAccessGroupsGetCurrent(void) {
124 SecurityClient
*client
= SecSecurityClientGet();
125 assert(client
&& client
->accessGroups
);
126 return client
->accessGroups
;
130 void SecAccessGroupsSetCurrent(CFArrayRef accessGroups
) {
131 // Not thread safe at all, but OK because it is meant to be used only by tests.
132 gClient
.accessGroups
= accessGroups
;
135 #if !TARGET_OS_IPHONE
136 static bool securityd_in_system_context(void) {
137 static bool runningInSystemContext
;
138 static dispatch_once_t onceToken
;
139 dispatch_once(&onceToken
, ^{
140 runningInSystemContext
= (getuid() == 0);
141 if (!runningInSystemContext
) {
143 if (vproc_swap_string(NULL
, VPROC_GSK_MGR_NAME
, NULL
, &manager
) == NULL
) {
144 runningInSystemContext
= (!strcmp(manager
, VPROCMGR_SESSION_SYSTEM
) ||
145 !strcmp(manager
, VPROCMGR_SESSION_LOGINWINDOW
));
150 return runningInSystemContext
;
154 static const char *securityd_service_name(void) {
155 return kSecuritydXPCServiceName
;
158 #define SECURITY_TARGET_UID_UNSET ((uid_t)-1)
159 static uid_t securityd_target_uid
= SECURITY_TARGET_UID_UNSET
;
162 _SecSetSecuritydTargetUID(uid_t uid
)
164 securityd_target_uid
= uid
;
168 static xpc_connection_t
securityd_create_connection(const char *name
, uid_t target_uid
, uint64_t flags
) {
169 const char *serviceName
= name
;
171 serviceName
= securityd_service_name();
173 xpc_connection_t connection
;
174 connection
= xpc_connection_create_mach_service(serviceName
, NULL
, flags
);
175 xpc_connection_set_event_handler(connection
, ^(xpc_object_t event
) {
176 const char *description
= xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
);
177 secnotice("xpc", "got event: %s", description
);
179 if (target_uid
!= SECURITY_TARGET_UID_UNSET
) {
180 xpc_connection_set_target_uid(connection
, target_uid
);
182 xpc_connection_resume(connection
);
186 static xpc_connection_t sSecuritydConnection
;
187 static xpc_connection_t sTrustdConnection
;
189 static xpc_connection_t
securityd_connection(void) {
190 static dispatch_once_t once
;
191 dispatch_once(&once
, ^{
192 sSecuritydConnection
= securityd_create_connection(kSecuritydXPCServiceName
, securityd_target_uid
, 0);
194 return sSecuritydConnection
;
197 static xpc_connection_t
trustd_connection(void) {
198 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
199 static dispatch_once_t once
;
200 dispatch_once(&once
, ^{
201 bool sysCtx
= securityd_in_system_context();
202 uint64_t flags
= (sysCtx
) ? XPC_CONNECTION_MACH_SERVICE_PRIVILEGED
: 0;
203 const char *serviceName
= (sysCtx
) ? kTrustdXPCServiceName
: kTrustdAgentXPCServiceName
;
204 sTrustdConnection
= securityd_create_connection(serviceName
, SECURITY_TARGET_UID_UNSET
, flags
);
206 return sTrustdConnection
;
208 static dispatch_once_t once
;
209 dispatch_once(&once
, ^{
210 sTrustdConnection
= securityd_create_connection(kTrustdXPCServiceName
, SECURITY_TARGET_UID_UNSET
, 0);
212 return sTrustdConnection
;
216 static bool is_trust_operation(enum SecXPCOperation op
) {
218 case sec_trust_store_contains_id
:
219 case sec_trust_store_set_trust_settings_id
:
220 case sec_trust_store_remove_certificate_id
:
221 case sec_trust_evaluate_id
:
222 case sec_trust_store_copy_all_id
:
223 case sec_trust_store_copy_usage_constraints_id
:
224 case sec_ocsp_cache_flush_id
:
225 case sec_ota_pki_trust_store_version_id
:
226 case kSecXPCOpOTAGetEscrowCertificates
:
227 case kSecXPCOpOTAPKIGetNewAsset
:
228 case kSecXPCOpTLSAnaltyicsReport
:
236 static xpc_connection_t
securityd_connection_for_operation(enum SecXPCOperation op
) {
237 bool isTrustOp
= is_trust_operation(op
);
238 #if SECTRUST_VERBOSE_DEBUG
240 bool sysCtx
= securityd_in_system_context();
241 const char *serviceName
= (sysCtx
) ? kTrustdXPCServiceName
: kTrustdAgentXPCServiceName
;
242 syslog(LOG_ERR
, "Will connect to: %s (op=%d)",
243 (isTrustOp
) ? serviceName
: kSecuritydXPCServiceName
, (int)op
);
246 return (isTrustOp
) ? trustd_connection() : securityd_connection();
249 // NOTE: This is not thread safe, but this SPI is for testing only.
250 void SecServerSetTrustdMachServiceName(const char *name
) {
251 // Make sure sSecXPCServer.queue exists.
254 xpc_connection_t oldConection
= sTrustdConnection
;
255 sTrustdConnection
= securityd_create_connection(name
, SECURITY_TARGET_UID_UNSET
, 0);
257 xpc_release(oldConection
);
261 #define SECURITYD_MAX_XPC_TRIES 4
262 // Per <rdar://problem/17829836> N61/12A342: Audio Playback... for why this needs to be at least 3, so we made it 4.
265 _securityd_process_message_reply(xpc_object_t
*reply
,
267 xpc_connection_t connection
,
270 if (xpc_get_type(*reply
) != XPC_TYPE_ERROR
) {
274 if (*reply
== XPC_ERROR_CONNECTION_INTERRUPTED
|| *reply
== XPC_ERROR_CONNECTION_INVALID
) {
275 code
= kSecXPCErrorConnectionFailed
;
276 seccritical("Failed to talk to %s after %d attempts.",
277 (is_trust_operation((enum SecXPCOperation
)operation
)) ? "trustd" :
283 SECURITYD_MAX_XPC_TRIES
);
284 } else if (*reply
== XPC_ERROR_TERMINATION_IMMINENT
) {
285 code
= kSecXPCErrorUnknown
;
287 code
= kSecXPCErrorUnknown
;
290 char *conn_desc
= xpc_copy_description(connection
);
291 const char *description
= xpc_dictionary_get_string(*reply
, XPC_ERROR_KEY_DESCRIPTION
);
292 SecCFCreateErrorWithFormat(code
, sSecXPCErrorDomain
, NULL
, error
, NULL
, CFSTR("%s: %s"), conn_desc
, description
);
302 securityd_message_with_reply_sync(xpc_object_t message
, CFErrorRef
*error
)
304 xpc_object_t reply
= NULL
;
305 uint64_t operation
= xpc_dictionary_get_uint64(message
, kSecXPCKeyOperation
);
306 xpc_connection_t connection
= securityd_connection_for_operation((enum SecXPCOperation
)operation
);
308 const int max_tries
= SECURITYD_MAX_XPC_TRIES
;
310 unsigned int tries_left
= max_tries
;
312 if (reply
) xpc_release(reply
);
313 reply
= xpc_connection_send_message_with_reply_sync(connection
, message
);
314 } while (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
&& --tries_left
> 0);
316 _securityd_process_message_reply(&reply
, error
, connection
, operation
);
322 _securityd_message_with_reply_async_inner(xpc_object_t message
,
323 dispatch_queue_t replyq
,
324 securityd_handler_t handler
,
327 uint64_t operation
= xpc_dictionary_get_uint64(message
, kSecXPCKeyOperation
);
328 xpc_connection_t connection
= securityd_connection_for_operation((enum SecXPCOperation
)operation
);
331 dispatch_retain(replyq
);
332 securityd_handler_t handlerCopy
= Block_copy(handler
);
333 xpc_connection_send_message_with_reply(connection
, message
, replyq
, ^(xpc_object_t _Nonnull reply
) {
334 if (reply
== XPC_ERROR_CONNECTION_INTERRUPTED
&& tries_left
> 0) {
335 _securityd_message_with_reply_async_inner(message
, replyq
, handlerCopy
, tries_left
- 1);
337 CFErrorRef error
= NULL
;
338 _securityd_process_message_reply(&reply
, &error
, connection
, operation
);
339 handlerCopy(reply
, error
);
340 CFReleaseNull(error
);
342 xpc_release(message
);
343 dispatch_release(replyq
);
344 Block_release(handlerCopy
);
349 securityd_message_with_reply_async(xpc_object_t message
,
350 dispatch_queue_t replyq
,
351 securityd_handler_t handler
)
353 _securityd_message_with_reply_async_inner(message
, replyq
, handler
, SECURITYD_MAX_XPC_TRIES
);
358 securityd_create_message(enum SecXPCOperation op
, CFErrorRef
* error
)
360 xpc_object_t message
= xpc_dictionary_create(NULL
, NULL
, 0);
362 xpc_dictionary_set_uint64(message
, kSecXPCKeyOperation
, op
);
364 SecCFCreateError(kSecXPCErrorConnectionFailed
, sSecXPCErrorDomain
,
365 CFSTR("xpc_dictionary_create returned NULL"), NULL
, error
);
370 // Return true if there is no error in message, return false and set *error if there is.
371 bool securityd_message_no_error(xpc_object_t message
, CFErrorRef
*error
) {
372 xpc_object_t xpc_error
= NULL
;
376 xpc_error
= xpc_dictionary_get_value(message
, kSecXPCKeyError
);
377 if (xpc_error
== NULL
)
380 CFErrorRef localError
= SecCreateCFErrorWithXPCObject(xpc_error
);
383 secdebug("xpc", "Talking to securityd failed with error: %@", localError
);
386 uint64_t operation
= xpc_dictionary_get_uint64(message
, kSecXPCKeyOperation
);
388 secdebug("xpc", "Talking to %s failed with error: %@",
389 (is_trust_operation((enum SecXPCOperation
)operation
)) ? "trustd" : "secd", localError
);
395 CFReleaseSafe(localError
);
400 bool securityd_send_sync_and_do(enum SecXPCOperation op
, CFErrorRef
*error
,
401 bool (^add_to_message
)(xpc_object_t message
, CFErrorRef
* error
),
402 bool (^handle_response
)(xpc_object_t _Nonnull response
, CFErrorRef
* error
)) {
403 xpc_object_t message
= securityd_create_message(op
, error
);
406 if (!add_to_message
|| add_to_message(message
, error
)) {
407 xpc_object_t response
= securityd_message_with_reply_sync(message
, error
);
409 if (securityd_message_no_error(response
, error
)) {
410 ok
= (!handle_response
|| handle_response(response
, error
));
412 xpc_release(response
);
415 xpc_release(message
);
421 void securityd_send_async_and_do(enum SecXPCOperation op
, dispatch_queue_t replyq
,
422 bool (^add_to_message
)(xpc_object_t message
, CFErrorRef
* error
),
423 securityd_handler_t handler
) {
424 CFErrorRef error
= NULL
;
425 xpc_object_t message
= securityd_create_message(op
, &error
);
426 if (message
== NULL
) {
427 handler(NULL
, error
);
428 CFReleaseNull(error
);
432 if (add_to_message
!= NULL
) {
433 if (!add_to_message(message
, &error
)) {
434 handler(NULL
, error
);
435 xpc_release(message
);
436 CFReleaseNull(error
);
441 securityd_message_with_reply_async(message
, replyq
, ^(xpc_object_t reply
, CFErrorRef error2
) {
442 if (error2
!= NULL
) {
443 handler(NULL
, error2
);
446 CFErrorRef error3
= NULL
;
447 if (!securityd_message_no_error(reply
, &error3
)) {
448 handler(NULL
, error3
);
449 CFReleaseNull(error3
);
452 handler(reply
, NULL
);
454 xpc_release(message
);
459 _SecSecuritydCopyWhoAmI(CFErrorRef
*error
)
461 CFDictionaryRef reply
= NULL
;
462 xpc_object_t message
= securityd_create_message(kSecXPCOpWhoAmI
, error
);
464 xpc_object_t response
= securityd_message_with_reply_sync(message
, error
);
466 reply
= _CFXPCCreateCFObjectFromXPCObject(response
);
467 xpc_release(response
);
469 secerror("Securityd failed getting whoamid with error: %@",
470 error
? *error
: NULL
);
472 xpc_release(message
);
478 _SecSyncBubbleTransfer(CFArrayRef services
, uid_t uid
, CFErrorRef
*error
)
480 xpc_object_t message
;
483 message
= securityd_create_message(kSecXPCOpTransmogrifyToSyncBubble
, error
);
485 xpc_dictionary_set_int64(message
, "uid", uid
);
486 if (SecXPCDictionarySetPList(message
, "services", services
, error
)) {
487 xpc_object_t response
= securityd_message_with_reply_sync(message
, error
);
489 reply
= xpc_dictionary_get_bool(response
, kSecXPCKeyResult
);
491 securityd_message_no_error(response
, error
);
492 xpc_release(response
);
494 xpc_release(message
);
501 _SecSystemKeychainTransfer(CFErrorRef
*error
)
503 xpc_object_t message
;
506 message
= securityd_create_message(kSecXPCOpTransmogrifyToSystemKeychain
, error
);
508 xpc_object_t response
= securityd_message_with_reply_sync(message
, error
);
510 reply
= xpc_dictionary_get_bool(response
, kSecXPCKeyResult
);
512 securityd_message_no_error(response
, error
);
513 xpc_release(response
);
515 xpc_release(message
);
521 _SecSyncDeleteUserViews(uid_t uid
, CFErrorRef
*error
)
523 xpc_object_t message
;
526 message
= securityd_create_message(kSecXPCOpDeleteUserView
, error
);
528 xpc_dictionary_set_int64(message
, "uid", uid
);
530 xpc_object_t response
= securityd_message_with_reply_sync(message
, error
);
532 reply
= xpc_dictionary_get_bool(response
, kSecXPCKeyResult
);
534 securityd_message_no_error(response
, error
);
535 xpc_release(response
);
537 xpc_release(message
);
542 XPC_RETURNS_RETAINED xpc_endpoint_t
543 _SecSecuritydCopyEndpoint(enum SecXPCOperation op
, CFErrorRef
*error
)
545 xpc_endpoint_t endpoint
= NULL
;
546 xpc_object_t message
= securityd_create_message(op
, error
);
548 xpc_object_t response
= securityd_message_with_reply_sync(message
, error
);
550 endpoint
= xpc_dictionary_get_value(response
, kSecXPCKeyEndpoint
);
552 if(xpc_get_type(endpoint
) != XPC_TYPE_ENDPOINT
) {
553 secerror("endpoint was not an endpoint");
556 xpc_retain(endpoint
);
559 secerror("endpoint was null");
561 xpc_release(response
);
563 secerror("Securityd failed getting endpoint with error: %@", error
? *error
: NULL
);
565 xpc_release(message
);
571 XPC_RETURNS_RETAINED xpc_endpoint_t
572 _SecSecuritydCopyCKKSEndpoint(CFErrorRef
*error
)
577 XPC_RETURNS_RETAINED xpc_endpoint_t
578 _SecSecuritydCopyKeychainControlEndpoint(CFErrorRef
* error
)
580 return _SecSecuritydCopyEndpoint(kSecXPCOpKeychainControlEndpoint
, error
);
583 XPC_RETURNS_RETAINED xpc_endpoint_t
584 _SecSecuritydCopySFKeychainEndpoint(CFErrorRef
* error
)
586 return _SecSecuritydCopyEndpoint(kSecXPCOpSFKeychainEndpoint
, error
);