2 * Copyright (c) 2017-2018 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 #include <dirhelper_priv.h>
28 #include <sys/types.h>
31 #include <xpc/private.h>
33 #include <CoreFoundation/CFStream.h>
34 #include <os/assumes.h>
36 #include <Security/SecuritydXPC.h>
37 #include <Security/SecTrustStore.h>
38 #include <Security/SecCertificateInternal.h>
39 #include <Security/SecEntitlements.h>
40 #include <Security/SecTrustInternal.h>
41 #include <Security/SecPolicyPriv.h>
42 #include <Security/SecItem.h>
43 #include <Security/SecItemPriv.h>
45 #include <ipc/securityd_client.h>
46 #include <ipc/server_entitlement_helpers.h>
47 #include <utilities/SecCFWrappers.h>
48 #include <utilities/SecDb.h>
49 #include <utilities/SecFileLocations.h>
50 #include <utilities/debugging.h>
51 #include <utilities/SecXPCError.h>
52 #include <securityd/SecOCSPCache.h>
53 #include <securityd/SecTrustStoreServer.h>
54 #include <securityd/SecPinningDb.h>
55 #include <securityd/SecPolicyServer.h>
56 #include <securityd/SecRevocationDb.h>
57 #include <securityd/SecTrustServer.h>
58 #include <securityd/spi.h>
59 #include <securityd/SecTrustLoggingServer.h>
62 #include <Security/SecTaskPriv.h>
63 #include <login/SessionAgentStatusCom.h>
64 #include <trustd/macOS/SecTrustOSXEntryPoints.h>
67 #include "OTATrustUtilities.h"
69 static struct trustd trustd_spi
= {
70 .sec_trust_store_for_domain
= SecTrustStoreForDomainName
,
71 .sec_trust_store_contains
= SecTrustStoreContainsCertificateWithDigest
,
72 .sec_trust_store_set_trust_settings
= _SecTrustStoreSetTrustSettings
,
73 .sec_trust_store_remove_certificate
= SecTrustStoreRemoveCertificateWithDigest
,
74 .sec_truststore_remove_all
= _SecTrustStoreRemoveAll
,
75 .sec_trust_evaluate
= SecTrustServerEvaluate
,
76 .sec_ota_pki_trust_store_version
= SecOTAPKIGetCurrentTrustStoreVersion
,
77 .ota_CopyEscrowCertificates
= SecOTAPKICopyCurrentEscrowCertificates
,
78 .sec_ota_pki_get_new_asset
= SecOTAPKISignalNewAsset
,
79 .sec_trust_store_copy_all
= _SecTrustStoreCopyAll
,
80 .sec_trust_store_copy_usage_constraints
= _SecTrustStoreCopyUsageConstraints
,
81 .sec_ocsp_cache_flush
= SecOCSPCacheFlush
,
82 .sec_tls_analytics_report
= SecTLSAnalyticsReport
,
85 static bool SecXPCDictionarySetChainOptional(xpc_object_t message
, const char *key
, CFArrayRef path
, CFErrorRef
*error
) {
88 __block xpc_object_t xpc_chain
= NULL
;
89 require_action_quiet(xpc_chain
= xpc_array_create(NULL
, 0), exit
, SecError(errSecParam
, error
, CFSTR("xpc_array_create failed")));
90 CFArrayForEach(path
, ^(const void *value
) {
91 SecCertificateRef cert
= (SecCertificateRef
)value
;
92 if (xpc_chain
&& !SecCertificateAppendToXPCArray(cert
, xpc_chain
, error
)) {
93 xpc_release(xpc_chain
);
102 xpc_dictionary_set_value(message
, key
, xpc_chain
);
103 xpc_release(xpc_chain
);
107 static SecCertificateRef
SecXPCDictionaryCopyCertificate(xpc_object_t message
, const char *key
, CFErrorRef
*error
) {
109 const void *bytes
= xpc_dictionary_get_data(message
, key
, &length
);
111 SecCertificateRef certificate
= SecCertificateCreateWithBytes(kCFAllocatorDefault
, bytes
, length
);
114 SecError(errSecDecode
, error
, CFSTR("object for key %s failed to create certificate from data"), key
);
116 SecError(errSecParam
, error
, CFSTR("object for key %s missing"), key
);
121 static bool SecXPCDictionaryCopyCertificates(xpc_object_t message
, const char *key
, CFArrayRef
*certificates
, CFErrorRef
*error
) {
122 xpc_object_t xpc_certificates
= xpc_dictionary_get_value(message
, key
);
123 if (!xpc_certificates
)
124 return SecError(errSecAllocate
, error
, CFSTR("no certs for key %s"), key
);
125 *certificates
= SecCertificateXPCArrayCopyArray(xpc_certificates
, error
);
126 return *certificates
;
129 static bool SecXPCDictionaryCopyCertificatesOptional(xpc_object_t message
, const char *key
, CFArrayRef
*certificates
, CFErrorRef
*error
) {
130 xpc_object_t xpc_certificates
= xpc_dictionary_get_value(message
, key
);
131 if (!xpc_certificates
) {
132 *certificates
= NULL
;
135 *certificates
= SecCertificateXPCArrayCopyArray(xpc_certificates
, error
);
136 return *certificates
;
139 static bool SecXPCDictionaryCopyPoliciesOptional(xpc_object_t message
, const char *key
, CFArrayRef
*policies
, CFErrorRef
*error
) {
140 xpc_object_t xpc_policies
= xpc_dictionary_get_value(message
, key
);
146 *policies
= SecPolicyXPCArrayCopyArray(xpc_policies
, error
);
147 return *policies
!= NULL
;
150 // Returns error if entitlement isn't present.
152 EntitlementPresentAndTrue(uint64_t op
, SecTaskRef clientTask
, CFStringRef entitlement
, CFErrorRef
*error
)
154 if (!SecTaskGetBooleanValueForEntitlement(clientTask
, entitlement
)) {
155 SecError(errSecMissingEntitlement
, error
, CFSTR("%@: %@ lacks entitlement %@"), SOSCCGetOperationDescription((enum SecXPCOperation
)op
), clientTask
, entitlement
);
161 static SecTrustStoreRef
SecXPCDictionaryGetTrustStore(xpc_object_t message
, const char *key
, CFErrorRef
*error
) {
162 SecTrustStoreRef ts
= NULL
;
163 CFStringRef domain
= SecXPCDictionaryCopyString(message
, key
, error
);
165 ts
= SecTrustStoreForDomainName(domain
, error
);
171 static bool SecXPCTrustStoreContains(xpc_object_t event
, xpc_object_t reply
, CFErrorRef
*error
) {
173 SecTrustStoreRef ts
= SecXPCDictionaryGetTrustStore(event
, kSecXPCKeyDomain
, error
);
175 CFDataRef digest
= SecXPCDictionaryCopyData(event
, kSecXPCKeyDigest
, error
);
178 if (SecTrustStoreContainsCertificateWithDigest(ts
, digest
, &contains
, error
)) {
179 xpc_dictionary_set_bool(reply
, kSecXPCKeyResult
, contains
);
182 CFReleaseNull(digest
);
188 static bool SecXPCTrustStoreSetTrustSettings(xpc_object_t event
, xpc_object_t reply
, CFErrorRef
*error
) {
189 bool noError
= false;
190 SecTrustStoreRef ts
= SecXPCDictionaryGetTrustStore(event
, kSecXPCKeyDomain
, error
);
192 SecCertificateRef certificate
= SecXPCDictionaryCopyCertificate(event
, kSecXPCKeyCertificate
, error
);
194 CFTypeRef trustSettingsDictOrArray
= NULL
;
195 if (SecXPCDictionaryCopyPListOptional(event
, kSecXPCKeySettings
, &trustSettingsDictOrArray
, error
)) {
196 bool result
= _SecTrustStoreSetTrustSettings(ts
, certificate
, trustSettingsDictOrArray
, error
);
197 xpc_dictionary_set_bool(reply
, kSecXPCKeyResult
, result
);
199 CFReleaseSafe(trustSettingsDictOrArray
);
201 CFReleaseNull(certificate
);
207 static bool SecXPCTrustStoreRemoveCertificate(xpc_object_t event
, xpc_object_t reply
, CFErrorRef
*error
) {
208 SecTrustStoreRef ts
= SecXPCDictionaryGetTrustStore(event
, kSecXPCKeyDomain
, error
);
210 CFDataRef digest
= SecXPCDictionaryCopyData(event
, kSecXPCKeyDigest
, error
);
212 bool result
= SecTrustStoreRemoveCertificateWithDigest(ts
, digest
, error
);
213 xpc_dictionary_set_bool(reply
, kSecXPCKeyResult
, result
);
214 CFReleaseNull(digest
);
221 static bool SecXPCTrustStoreCopyAll(xpc_object_t event
, xpc_object_t reply
, CFErrorRef
*error
) {
222 SecTrustStoreRef ts
= SecXPCDictionaryGetTrustStore(event
, kSecXPCKeyDomain
, error
);
224 CFArrayRef trustStoreContents
= NULL
;
225 if(_SecTrustStoreCopyAll(ts
, &trustStoreContents
, error
) && trustStoreContents
) {
226 SecXPCDictionarySetPList(reply
, kSecXPCKeyResult
, trustStoreContents
, error
);
227 CFReleaseNull(trustStoreContents
);
234 static bool SecXPCTrustStoreCopyUsageConstraints(xpc_object_t event
, xpc_object_t reply
, CFErrorRef
*error
) {
236 SecTrustStoreRef ts
= SecXPCDictionaryGetTrustStore(event
, kSecXPCKeyDomain
, error
);
238 CFDataRef digest
= SecXPCDictionaryCopyData(event
, kSecXPCKeyDigest
, error
);
240 CFArrayRef usageConstraints
= NULL
;
241 if(_SecTrustStoreCopyUsageConstraints(ts
, digest
, &usageConstraints
, error
) && usageConstraints
) {
242 SecXPCDictionarySetPList(reply
, kSecXPCKeyResult
, usageConstraints
, error
);
243 CFReleaseNull(usageConstraints
);
246 CFReleaseNull(digest
);
252 static bool SecXPC_OCSPCacheFlush(xpc_object_t __unused event
, xpc_object_t __unused reply
, CFErrorRef
*error
) {
253 if(SecOCSPCacheFlush(error
)) {
259 static bool SecXPC_OTAPKI_GetAssetVersion(xpc_object_t __unused event
, xpc_object_t reply
, CFErrorRef
*error
) {
260 xpc_dictionary_set_uint64(reply
, kSecXPCKeyResult
, SecOTAPKIGetCurrentTrustStoreVersion(error
));
264 static bool SecXPC_OTAPKI_GetEscrowCertificates(xpc_object_t event
, xpc_object_t reply
, CFErrorRef
*error
) {
266 uint32_t escrowRootType
= (uint32_t)xpc_dictionary_get_uint64(event
, "escrowType");
267 CFArrayRef array
= SecOTAPKICopyCurrentEscrowCertificates(escrowRootType
, error
);
269 xpc_object_t xpc_array
= _CFXPCCreateXPCObjectFromCFObject(array
);
270 xpc_dictionary_set_value(reply
, kSecXPCKeyResult
, xpc_array
);
271 xpc_release(xpc_array
);
274 CFReleaseNull(array
);
278 static bool SecXPC_OTAPKI_GetNewAsset(xpc_object_t __unused event
, xpc_object_t reply
, CFErrorRef
*error
) {
279 xpc_dictionary_set_uint64(reply
, kSecXPCKeyResult
, SecOTAPKISignalNewAsset(error
));
283 static bool SecXPC_TLS_AnalyticsReport(xpc_object_t event
, xpc_object_t reply
, CFErrorRef
*error
) {
284 xpc_object_t attributes
= xpc_dictionary_get_dictionary(event
, kSecTrustEventAttributesKey
);
285 CFStringRef eventName
= SecXPCDictionaryCopyString(event
, kSecTrustEventNameKey
, error
);
287 if (attributes
&& eventName
) {
288 result
= SecTLSAnalyticsReport(eventName
, attributes
, error
);
290 xpc_dictionary_set_bool(reply
, kSecXPCKeyResult
, result
);
291 CFReleaseNull(eventName
);
295 typedef bool(*SecXPCOperationHandler
)(xpc_object_t event
, xpc_object_t reply
, CFErrorRef
*error
);
298 CFStringRef entitlement
;
299 SecXPCOperationHandler handler
;
300 } SecXPCServerOperation
;
302 struct trustd_operations
{
303 SecXPCServerOperation trust_store_contains
;
304 SecXPCServerOperation trust_store_set_trust_settings
;
305 SecXPCServerOperation trust_store_remove_certificate
;
306 SecXPCServerOperation trust_store_copy_all
;
307 SecXPCServerOperation trust_store_copy_usage_constraints
;
308 SecXPCServerOperation ocsp_cache_flush
;
309 SecXPCServerOperation ota_pki_trust_store_version
;
310 SecXPCServerOperation ota_pki_get_escrow_certs
;
311 SecXPCServerOperation ota_pki_get_new_asset
;
312 SecXPCServerOperation tls_analytics_report
;
315 static struct trustd_operations trustd_ops
= {
316 .trust_store_contains
= { NULL
, SecXPCTrustStoreContains
},
317 .trust_store_set_trust_settings
= { kSecEntitlementModifyAnchorCertificates
, SecXPCTrustStoreSetTrustSettings
},
318 .trust_store_remove_certificate
= { kSecEntitlementModifyAnchorCertificates
, SecXPCTrustStoreRemoveCertificate
},
319 .trust_store_copy_all
= { kSecEntitlementModifyAnchorCertificates
, SecXPCTrustStoreCopyAll
},
320 .trust_store_copy_usage_constraints
= { kSecEntitlementModifyAnchorCertificates
, SecXPCTrustStoreCopyUsageConstraints
},
321 .ocsp_cache_flush
= { NULL
, SecXPC_OCSPCacheFlush
},
322 .ota_pki_trust_store_version
= { NULL
, SecXPC_OTAPKI_GetAssetVersion
},
323 .ota_pki_get_escrow_certs
= { NULL
, SecXPC_OTAPKI_GetEscrowCertificates
},
324 .ota_pki_get_new_asset
= { NULL
, SecXPC_OTAPKI_GetNewAsset
},
325 .tls_analytics_report
= { NULL
, SecXPC_TLS_AnalyticsReport
},
328 static void trustd_xpc_dictionary_handler(const xpc_connection_t connection
, xpc_object_t event
) {
329 xpc_type_t type
= xpc_get_type(event
);
330 __block CFErrorRef error
= NULL
;
331 xpc_object_t xpcError
= NULL
;
332 xpc_object_t replyMessage
= NULL
;
333 CFDataRef clientAuditToken
= NULL
;
334 CFArrayRef domains
= NULL
;
335 SecurityClient client
= {
337 .accessGroups
= NULL
,
339 .uid
= xpc_connection_get_euid(connection
),
340 .allowSystemKeychain
= true,
341 .allowSyncBubbleKeychain
= false,
342 .isNetworkExtension
= false,
343 .canAccessNetworkExtensionAccessGroups
= false,
345 .inMultiUser
= false,
349 secdebug("serverxpc", "entering");
350 if (type
== XPC_TYPE_DICTIONARY
) {
351 // TODO: Find out what we're dispatching.
352 replyMessage
= xpc_dictionary_create_reply(event
);
355 uint64_t operation
= xpc_dictionary_get_uint64(event
, kSecXPCKeyOperation
);
357 audit_token_t auditToken
= {};
358 xpc_connection_get_audit_token(connection
, &auditToken
);
360 client
.task
= SecTaskCreateWithAuditToken(kCFAllocatorDefault
, auditToken
);
361 clientAuditToken
= CFDataCreate(kCFAllocatorDefault
, (const UInt8
*)&auditToken
, sizeof(auditToken
));
362 client
.accessGroups
= SecTaskCopyAccessGroups(client
.task
);
364 secinfo("serverxpc", "XPC [%@] operation: %@ (%" PRIu64
")", client
.task
, SOSCCGetOperationDescription((enum SecXPCOperation
)operation
), operation
);
366 if (operation
== sec_trust_evaluate_id
) {
367 CFArrayRef certificates
= NULL
, anchors
= NULL
, policies
= NULL
, responses
= NULL
, scts
= NULL
, trustedLogs
= NULL
, exceptions
= NULL
;
368 bool anchorsOnly
= xpc_dictionary_get_bool(event
, kSecTrustAnchorsOnlyKey
);
369 bool keychainsAllowed
= xpc_dictionary_get_bool(event
, kSecTrustKeychainsAllowedKey
);
371 if (SecXPCDictionaryCopyCertificates(event
, kSecTrustCertificatesKey
, &certificates
, &error
) &&
372 SecXPCDictionaryCopyCertificatesOptional(event
, kSecTrustAnchorsKey
, &anchors
, &error
) &&
373 SecXPCDictionaryCopyPoliciesOptional(event
, kSecTrustPoliciesKey
, &policies
, &error
) &&
374 SecXPCDictionaryCopyCFDataArrayOptional(event
, kSecTrustResponsesKey
, &responses
, &error
) &&
375 SecXPCDictionaryCopyCFDataArrayOptional(event
, kSecTrustSCTsKey
, &scts
, &error
) &&
376 SecXPCDictionaryCopyArrayOptional(event
, kSecTrustTrustedLogsKey
, &trustedLogs
, &error
) &&
377 SecXPCDictionaryGetDouble(event
, kSecTrustVerifyDateKey
, &verifyTime
, &error
) &&
378 SecXPCDictionaryCopyArrayOptional(event
, kSecTrustExceptionsKey
, &exceptions
, &error
)) {
379 // If we have no error yet, capture connection and reply in block and properly retain them.
380 xpc_retain(connection
);
381 CFRetainSafe(client
.task
);
382 CFRetainSafe(clientAuditToken
);
384 // Clear replyMessage so we don't send a synchronous reply.
385 xpc_object_t asyncReply
= replyMessage
;
388 SecTrustServerEvaluateBlock(clientAuditToken
, certificates
, anchors
, anchorsOnly
, keychainsAllowed
, policies
,
389 responses
, scts
, trustedLogs
, verifyTime
, client
.accessGroups
, exceptions
,
390 ^(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, CFArrayRef chain
,
391 CFErrorRef replyError
) {
392 // Send back reply now
394 CFRetain(replyError
);
396 xpc_dictionary_set_int64(asyncReply
, kSecTrustResultKey
, tr
);
397 SecXPCDictionarySetPListOptional(asyncReply
, kSecTrustDetailsKey
, details
, &replyError
) &&
398 SecXPCDictionarySetPListOptional(asyncReply
, kSecTrustInfoKey
, info
, &replyError
) &&
399 SecXPCDictionarySetChainOptional(asyncReply
, kSecTrustChainKey
, chain
, &replyError
);
402 secdebug("ipc", "%@ %@ %@", client
.task
, SOSCCGetOperationDescription((enum SecXPCOperation
)operation
), replyError
);
403 xpc_object_t xpcReplyError
= SecCreateXPCObjectWithCFError(replyError
);
405 xpc_dictionary_set_value(asyncReply
, kSecXPCKeyError
, xpcReplyError
);
406 xpc_release(xpcReplyError
);
408 CFReleaseNull(replyError
);
410 secdebug("ipc", "%@ %@ responding %@", client
.task
, SOSCCGetOperationDescription((enum SecXPCOperation
)operation
), asyncReply
);
413 // Ensure that we remain dirty for two seconds after ending the client's transaction to avoid jetsam loops.
414 // Refer to rdar://problem/38044831 for more details.
415 static dispatch_queue_t dirty_timer_queue
= NULL
;
416 static dispatch_source_t dirty_timer
= NULL
;
417 static bool has_transcation
= false;
418 static dispatch_once_t onceToken
;
419 dispatch_once(&onceToken
, ^{
420 dirty_timer_queue
= dispatch_queue_create("dirty timer queue", DISPATCH_QUEUE_SERIAL
);
421 dirty_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, dirty_timer_queue
);
422 dispatch_source_set_event_handler(dirty_timer
, ^{
423 /* timer fired, end the transaction */
424 os_assumes(has_transcation
);
425 xpc_transaction_end();
426 has_transcation
= false;
430 dispatch_sync(dirty_timer_queue
, ^{
431 /* reset the timer for 2 seconds from now */
432 dispatch_source_set_timer(dirty_timer
, dispatch_time(DISPATCH_TIME_NOW
, 2 * NSEC_PER_SEC
),
433 DISPATCH_TIME_FOREVER
, 100 * NSEC_PER_MSEC
);
434 if (!has_transcation
) {
435 /* timer is not running/not holding a transaction, start transaction */
436 xpc_transaction_begin();
437 has_transcation
= true;
439 static dispatch_once_t onceToken2
;
440 dispatch_once(&onceToken2
, ^{
441 dispatch_resume(dirty_timer
);
445 xpc_connection_send_message(connection
, asyncReply
);
446 xpc_release(asyncReply
);
447 xpc_release(connection
);
448 CFReleaseSafe(client
.task
);
449 CFReleaseSafe(clientAuditToken
);
452 CFReleaseSafe(policies
);
453 CFReleaseSafe(anchors
);
454 CFReleaseSafe(certificates
);
455 CFReleaseSafe(responses
);
457 CFReleaseSafe(trustedLogs
);
458 CFReleaseSafe(exceptions
);
460 SecXPCServerOperation
*server_op
= NULL
;
462 case sec_trust_store_contains_id
:
463 server_op
= &trustd_ops
.trust_store_contains
;
465 case sec_trust_store_set_trust_settings_id
:
466 server_op
= &trustd_ops
.trust_store_set_trust_settings
;
468 case sec_trust_store_remove_certificate_id
:
469 server_op
= &trustd_ops
.trust_store_remove_certificate
;
471 case sec_trust_store_copy_all_id
:
472 server_op
= &trustd_ops
.trust_store_copy_all
;
474 case sec_trust_store_copy_usage_constraints_id
:
475 server_op
= &trustd_ops
.trust_store_copy_usage_constraints
;
477 case sec_ocsp_cache_flush_id
:
478 server_op
= &trustd_ops
.ocsp_cache_flush
;
480 case sec_ota_pki_trust_store_version_id
:
481 server_op
= &trustd_ops
.ota_pki_trust_store_version
;
483 case kSecXPCOpOTAGetEscrowCertificates
:
484 server_op
= &trustd_ops
.ota_pki_get_escrow_certs
;
486 case kSecXPCOpOTAPKIGetNewAsset
:
487 server_op
= &trustd_ops
.ota_pki_get_new_asset
;
489 case kSecXPCOpTLSAnaltyicsReport
:
490 server_op
= &trustd_ops
.tls_analytics_report
;
494 if (server_op
&& server_op
->handler
) {
495 bool entitled
= true;
496 if (server_op
->entitlement
) {
497 entitled
= EntitlementPresentAndTrue(operation
, client
.task
, server_op
->entitlement
, &error
);
500 (void)server_op
->handler(event
, replyMessage
, &error
);
507 if(SecErrorGetOSStatus(error
) == errSecItemNotFound
)
508 secdebug("ipc", "%@ %@ %@", client
.task
, SOSCCGetOperationDescription((enum SecXPCOperation
)operation
), error
);
509 else if (SecErrorGetOSStatus(error
) == errSecAuthNeeded
)
510 secwarning("Authentication is needed %@ %@ %@", client
.task
, SOSCCGetOperationDescription((enum SecXPCOperation
)operation
), error
);
512 secerror("%@ %@ %@", client
.task
, SOSCCGetOperationDescription((enum SecXPCOperation
)operation
), error
);
514 xpcError
= SecCreateXPCObjectWithCFError(error
);
516 xpc_dictionary_set_value(replyMessage
, kSecXPCKeyError
, xpcError
);
518 } else if (replyMessage
) {
519 secdebug("ipc", "%@ %@ responding %@", client
.task
, SOSCCGetOperationDescription((enum SecXPCOperation
)operation
), replyMessage
);
522 SecCFCreateErrorWithFormat(kSecXPCErrorUnexpectedType
, sSecXPCErrorDomain
, NULL
, &error
, 0, CFSTR("Messages expect to be xpc dictionary, got: %@"), event
);
523 secerror("%@: returning error: %@", client
.task
, error
);
524 xpcError
= SecCreateXPCObjectWithCFError(error
);
525 replyMessage
= xpc_create_reply_with_format(event
, "{%string: %value}", kSecXPCKeyError
, xpcError
);
529 xpc_connection_send_message(connection
, replyMessage
);
530 xpc_release(replyMessage
);
533 xpc_release(xpcError
);
534 CFReleaseSafe(error
);
535 CFReleaseSafe(client
.accessGroups
);
536 CFReleaseSafe(client
.musr
);
537 CFReleaseSafe(client
.task
);
538 CFReleaseSafe(domains
);
539 CFReleaseSafe(clientAuditToken
);
542 static void trustd_xpc_init(const char *service_name
)
544 secdebug("serverxpc", "start");
545 xpc_connection_t listener
= xpc_connection_create_mach_service(service_name
, NULL
, XPC_CONNECTION_MACH_SERVICE_LISTENER
);
547 seccritical("security failed to register xpc listener for %s, exiting", service_name
);
551 xpc_connection_set_event_handler(listener
, ^(xpc_object_t connection
) {
552 if (xpc_get_type(connection
) == XPC_TYPE_CONNECTION
) {
553 xpc_connection_set_event_handler(connection
, ^(xpc_object_t event
) {
554 if (xpc_get_type(event
) == XPC_TYPE_DICTIONARY
) {
555 xpc_retain(connection
);
557 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^{
558 trustd_xpc_dictionary_handler(connection
, event
);
560 xpc_release(connection
);
564 xpc_connection_resume(connection
);
567 xpc_connection_resume(listener
);
570 static void trustd_delete_old_sqlite_keychain_files(CFStringRef baseFilename
) {
571 WithPathInKeychainDirectory(baseFilename
, ^(const char *utf8String
) {
572 (void)remove(utf8String
);
574 CFStringRef shmFile
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@-shm"), baseFilename
);
575 WithPathInKeychainDirectory(shmFile
, ^(const char *utf8String
) {
576 (void)remove(utf8String
);
578 CFReleaseNull(shmFile
);
579 CFStringRef walFile
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@-wal"), baseFilename
);
580 WithPathInKeychainDirectory(walFile
, ^(const char *utf8String
) {
581 (void)remove(utf8String
);
583 CFReleaseNull(walFile
);
584 CFStringRef journalFile
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@-journal"), baseFilename
);
585 WithPathInKeychainDirectory(journalFile
, ^(const char *utf8String
) {
586 (void)remove(utf8String
);
588 CFReleaseNull(journalFile
);
592 static void trustd_delete_old_sqlite_user_cache_files(CFStringRef baseFilename
) {
593 WithPathInUserCacheDirectory(baseFilename
, ^(const char *utf8String
) {
594 (void)remove(utf8String
);
596 CFStringRef shmFile
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@-shm"), baseFilename
);
597 WithPathInUserCacheDirectory(shmFile
, ^(const char *utf8String
) {
598 (void)remove(utf8String
);
600 CFReleaseNull(shmFile
);
601 CFStringRef walFile
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@-wal"), baseFilename
);
602 WithPathInUserCacheDirectory(walFile
, ^(const char *utf8String
) {
603 (void)remove(utf8String
);
605 CFReleaseNull(walFile
);
606 CFStringRef journalFile
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@-journal"), baseFilename
);
607 WithPathInUserCacheDirectory(journalFile
, ^(const char *utf8String
) {
608 (void)remove(utf8String
);
610 CFReleaseNull(journalFile
);
612 #endif // TARGET_OS_OSX
614 static void trustd_delete_old_files(void) {
615 /* We try to clean up after ourselves, but don't care if we succeed. */
616 WithPathInRevocationInfoDirectory(CFSTR("update-current"), ^(const char *utf8String
) {
617 (void)remove(utf8String
);
619 WithPathInRevocationInfoDirectory(CFSTR("update-full"), ^(const char *utf8String
) {
620 (void)remove(utf8String
);
622 WithPathInRevocationInfoDirectory(CFSTR("update-full.gz"), ^(const char *utf8String
) {
623 (void)remove(utf8String
);
626 trustd_delete_old_sqlite_keychain_files(CFSTR("trustd_health_analytics.db"));
627 trustd_delete_old_sqlite_keychain_files(CFSTR("trust_analytics.db"));
628 trustd_delete_old_sqlite_keychain_files(CFSTR("TLS_analytics.db"));
630 trustd_delete_old_sqlite_user_cache_files(CFSTR("trustd_health_analytics.db"));
631 trustd_delete_old_sqlite_user_cache_files(CFSTR("trust_analytics.db"));
632 trustd_delete_old_sqlite_user_cache_files(CFSTR("TLS_analytics.db"));
633 #endif //TARGET_OS_IPHONE
637 static void trustd_delete_old_caches(void) {
638 /* We try to clean up after ourselves, but don't care if we succeed. */
639 trustd_delete_old_sqlite_keychain_files(CFSTR("ocspcache.sqlite3"));
640 trustd_delete_old_sqlite_keychain_files(CFSTR("caissuercache.sqlite3"));
643 static void trustd_sandbox(void) {
644 char buf
[PATH_MAX
] = "";
646 if (!_set_user_dir_suffix("com.apple.trustd") ||
647 confstr(_CS_DARWIN_USER_TEMP_DIR
, buf
, sizeof(buf
)) == 0 ||
648 (mkdir(buf
, 0700) && errno
!= EEXIST
)) {
649 secerror("failed to initialize temporary directory (%d): %s", errno
, strerror(errno
));
653 char *tempdir
= realpath(buf
, NULL
);
654 if (tempdir
== NULL
) {
655 secerror("failed to resolve temporary directory (%d): %s", errno
, strerror(errno
));
659 if (confstr(_CS_DARWIN_USER_CACHE_DIR
, buf
, sizeof(buf
)) == 0 ||
660 (mkdir(buf
, 0700) && errno
!= EEXIST
)) {
661 secerror("failed to initialize cache directory (%d): %s", errno
, strerror(errno
));
665 char *cachedir
= realpath(buf
, NULL
);
666 if (cachedir
== NULL
) {
667 secerror("failed to resolve cache directory (%d): %s", errno
, strerror(errno
));
671 const char *parameters
[] = {
673 "_DARWIN_CACHE_DIR", cachedir
,
677 char *sberror
= NULL
;
678 if (sandbox_init_with_parameters("com.apple.trustd", SANDBOX_NAMED
, parameters
, &sberror
) != 0) {
679 secerror("Failed to enter trustd sandbox: %{public}s", sberror
);
687 static void trustd_sandbox(void) {
688 char buf
[PATH_MAX
] = "";
689 _set_user_dir_suffix("com.apple.trustd");
690 confstr(_CS_DARWIN_USER_TEMP_DIR
, buf
, sizeof(buf
));
694 static void trustd_cfstream_init() {
695 CFReadStreamRef rs
= CFReadStreamCreateWithBytesNoCopy(kCFAllocatorDefault
, (const UInt8
*) "", 0, kCFAllocatorNull
);
696 CFReadStreamSetDispatchQueue(rs
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0));
697 CFReadStreamSetDispatchQueue(rs
, NULL
);
701 int main(int argc
, char *argv
[])
703 char *wait4debugger
= getenv("WAIT4DEBUGGER");
704 if (wait4debugger
&& !strcasecmp("YES", wait4debugger
)) {
705 seccritical("SIGSTOPing self, awaiting debugger");
706 kill(getpid(), SIGSTOP
);
707 seccritical("Again, for good luck (or bad debuggers)");
708 kill(getpid(), SIGSTOP
);
711 /* <rdar://problem/15792007> Users with network home folders are unable to use/save password for Mail/Cal/Contacts/websites
712 Our process doesn't realize DB connections get invalidated when network home directory users logout
713 and their home gets unmounted. Exit our process and start fresh when user logs back in.
716 int sessionstatechanged_tok
;
717 notify_register_dispatch(kSA_SessionStateChangedNotification
, &sessionstatechanged_tok
, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^(int token __unused
) {
718 // we could be a process running as root.
719 // However, since root never logs out this isn't an issue.
720 if (SASSessionStateForUser(getuid()) == kSA_state_loggingout_pointofnoreturn
) {
721 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, 3ull*NSEC_PER_SEC
), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^{
722 xpc_transaction_exit_clean();
729 /* Before we enter the sandbox, we need to delete the old caches kept in ~/Library/Keychains
730 * After we enter the sandbox, we won't be able to access them. */
731 trustd_delete_old_caches();
736 /* Also clean up old files in our sandbox. After sandboxing, so that user dir suffix is set. */
737 trustd_delete_old_files();
739 const char *serviceName
= kTrustdXPCServiceName
;
740 if (argc
> 1 && (!strcmp(argv
[1], "--agent"))) {
741 serviceName
= kTrustdAgentXPCServiceName
;
744 /* set up SQLite before some other component has a chance to create a database connection */
747 /* <rdar://problem/33635964> Force legacy CFStream run loop initialization before any NSURLSession usage */
748 trustd_cfstream_init();
750 gTrustd
= &trustd_spi
;
752 /* Initialize static content */
753 SecPolicyServerInitialize(); // set up callbacks for policy checks
754 SecRevocationDbInitialize(); // set up revocation database if it doesn't already exist, or needs to be replaced
755 SecPinningDbInitialize(); // set up the pinning database
757 SecTrustLegacySourcesListenForKeychainEvents(); // set up the legacy keychain event listeners (for cache invalidation)
760 /* We're ready now. Go. */
761 trustd_xpc_init(serviceName
);