#include <TargetConditionals.h>
#ifndef SEC_IOS_ON_OSX
#define SEC_IOS_ON_OSX 1
#endif // SEC_IOS_ON_OSX
#if TARGET_OS_OSX
#ifndef SECITEM_SHIM_OSX
#define SECITEM_SHIM_OSX 1
#endif // SECITEM_SHIM_OSX
#endif // TARGET_OS_OSX
#include <stdbool.h>
#include <sys/queue.h>
#include <syslog.h>
#include <vproc_priv.h>
#include <xpc/xpc.h>
#include <xpc/private.h>
#include <CoreFoundation/CoreFoundation.h>
#include <Security/SecItem.h>
#include <Security/SecBasePriv.h>
#include <Security/SecInternal.h>
#include <Security/SecuritydXPC.h>
#include <Security/SecTask.h>
#include <Security/SecItemPriv.h>
#include <utilities/debugging.h>
#include <utilities/SecCFError.h>
#include <utilities/SecXPCError.h>
#include <utilities/SecCFWrappers.h>
#include <utilities/SecDispatchRelease.h>
#include <utilities/SecDb.h> // TODO Fixme this gets us SecError().
#include <utilities/SecAKSWrappers.h>
#include <ipc/securityd_client.h>
#include "server_security_helpers.h"
struct securityd *gSecurityd;
struct trustd *gTrustd;
static CFArrayRef SecServerCopyAccessGroups(void) {
return CFArrayCreateForCFTypes(kCFAllocatorDefault,
#if NO_SERVER
CFSTR("test"),
CFSTR("apple"),
CFSTR("lockdown-identities"),
CFSTR("123456.test.group"),
CFSTR("123456.test.group2"),
CFSTR("com.apple.cfnetwork"),
#else
CFSTR("sync"),
#endif
CFSTR("com.apple.security.sos"),
CFSTR("com.apple.security.ckks"),
CFSTR("com.apple.security.sos-usercredential"),
CFSTR("com.apple.sbd"),
CFSTR("com.apple.lakitu"),
CFSTR("com.apple.security.securityd"),
kSecAttrAccessGroupToken,
NULL);
}
static SecurityClient gClient;
#if TARGET_OS_IOS
void
SecSecuritySetMusrMode(bool mode, uid_t uid, int activeUser)
{
gClient.inMultiUser = mode;
gClient.uid = uid;
gClient.activeUser = activeUser;
}
#endif
SecurityClient *
SecSecurityClientGet(void)
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
gClient.task = NULL;
gClient.accessGroups = SecServerCopyAccessGroups();
gClient.allowSystemKeychain = true;
gClient.allowSyncBubbleKeychain = true;
gClient.isNetworkExtension = false;
#if TARGET_OS_IPHONE
gClient.inMultiUser = false;
gClient.activeUser = 501;
#endif
});
return &gClient;
}
CFArrayRef SecAccessGroupsGetCurrent(void) {
SecurityClient *client = SecSecurityClientGet();
assert(client && client->accessGroups);
return client->accessGroups;
}
void SecAccessGroupsSetCurrent(CFArrayRef accessGroups) {
gClient.accessGroups = accessGroups;
}
#if !TARGET_OS_IPHONE
static bool securityd_in_system_context(void) {
static bool runningInSystemContext;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
runningInSystemContext = (getuid() == 0);
if (!runningInSystemContext) {
char *manager;
if (vproc_swap_string(NULL, VPROC_GSK_MGR_NAME, NULL, &manager) == NULL) {
runningInSystemContext = (!strcmp(manager, VPROCMGR_SESSION_SYSTEM) ||
!strcmp(manager, VPROCMGR_SESSION_LOGINWINDOW));
free(manager);
}
}
});
return runningInSystemContext;
}
#endif
static const char *securityd_service_name(void) {
return kSecuritydXPCServiceName;
}
#define SECURITY_TARGET_UID_UNSET ((uid_t)-1)
static uid_t securityd_target_uid = SECURITY_TARGET_UID_UNSET;
void
_SecSetSecuritydTargetUID(uid_t uid)
{
securityd_target_uid = uid;
}
static xpc_connection_t securityd_create_connection(const char *name, uid_t target_uid, uint64_t flags) {
const char *serviceName = name;
if (!serviceName) {
serviceName = securityd_service_name();
}
xpc_connection_t connection;
connection = xpc_connection_create_mach_service(serviceName, NULL, flags);
xpc_connection_set_event_handler(connection, ^(xpc_object_t event) {
const char *description = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION);
secnotice("xpc", "got event: %s", description);
});
if (target_uid != SECURITY_TARGET_UID_UNSET) {
xpc_connection_set_target_uid(connection, target_uid);
}
xpc_connection_resume(connection);
return connection;
}
static xpc_connection_t sSecuritydConnection;
static xpc_connection_t sTrustdConnection;
static xpc_connection_t securityd_connection(void) {
static dispatch_once_t once;
dispatch_once(&once, ^{
sSecuritydConnection = securityd_create_connection(kSecuritydXPCServiceName, securityd_target_uid, 0);
});
return sSecuritydConnection;
}
static xpc_connection_t trustd_connection(void) {
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
static dispatch_once_t once;
dispatch_once(&once, ^{
bool sysCtx = securityd_in_system_context();
uint64_t flags = (sysCtx) ? XPC_CONNECTION_MACH_SERVICE_PRIVILEGED : 0;
const char *serviceName = (sysCtx) ? kTrustdXPCServiceName : kTrustdAgentXPCServiceName;
sTrustdConnection = securityd_create_connection(serviceName, SECURITY_TARGET_UID_UNSET, flags);
});
return sTrustdConnection;
#else
static dispatch_once_t once;
dispatch_once(&once, ^{
sTrustdConnection = securityd_create_connection(kTrustdXPCServiceName, SECURITY_TARGET_UID_UNSET, 0);
});
return sTrustdConnection;
#endif
}
static bool is_trust_operation(enum SecXPCOperation op) {
switch (op) {
case sec_trust_store_contains_id:
case sec_trust_store_set_trust_settings_id:
case sec_trust_store_remove_certificate_id:
case sec_trust_evaluate_id:
case sec_trust_store_copy_all_id:
case sec_trust_store_copy_usage_constraints_id:
case sec_ocsp_cache_flush_id:
case sec_ota_pki_trust_store_version_id:
case kSecXPCOpOTAGetEscrowCertificates:
case kSecXPCOpOTAPKIGetNewAsset:
case kSecXPCOpTLSAnaltyicsReport:
return true;
default:
break;
}
return false;
}
static xpc_connection_t securityd_connection_for_operation(enum SecXPCOperation op) {
bool isTrustOp = is_trust_operation(op);
#if SECTRUST_VERBOSE_DEBUG
{
bool sysCtx = securityd_in_system_context();
const char *serviceName = (sysCtx) ? kTrustdXPCServiceName : kTrustdAgentXPCServiceName;
syslog(LOG_ERR, "Will connect to: %s (op=%d)",
(isTrustOp) ? serviceName : kSecuritydXPCServiceName, (int)op);
}
#endif
return (isTrustOp) ? trustd_connection() : securityd_connection();
}
void SecServerSetTrustdMachServiceName(const char *name) {
trustd_connection();
xpc_connection_t oldConection = sTrustdConnection;
sTrustdConnection = securityd_create_connection(name, SECURITY_TARGET_UID_UNSET, 0);
if (oldConection)
xpc_release(oldConection);
}
#define SECURITYD_MAX_XPC_TRIES 4
static bool
_securityd_process_message_reply(xpc_object_t *reply,
CFErrorRef *error,
xpc_connection_t connection,
uint64_t operation)
{
if (xpc_get_type(*reply) != XPC_TYPE_ERROR) {
return true;
}
CFIndex code = 0;
if (*reply == XPC_ERROR_CONNECTION_INTERRUPTED || *reply == XPC_ERROR_CONNECTION_INVALID) {
code = kSecXPCErrorConnectionFailed;
seccritical("Failed to talk to %s after %d attempts.",
(is_trust_operation((enum SecXPCOperation)operation)) ? "trustd" :
#if TARGET_OS_IPHONE
"securityd",
#else
"secd",
#endif
SECURITYD_MAX_XPC_TRIES);
} else if (*reply == XPC_ERROR_TERMINATION_IMMINENT) {
code = kSecXPCErrorUnknown;
} else {
code = kSecXPCErrorUnknown;
}
char *conn_desc = xpc_copy_description(connection);
const char *description = xpc_dictionary_get_string(*reply, XPC_ERROR_KEY_DESCRIPTION);
SecCFCreateErrorWithFormat(code, sSecXPCErrorDomain, NULL, error, NULL, CFSTR("%s: %s"), conn_desc, description);
free(conn_desc);
xpc_release(*reply);
*reply = NULL;
return false;
}
XPC_RETURNS_RETAINED
xpc_object_t
securityd_message_with_reply_sync(xpc_object_t message, CFErrorRef *error)
{
xpc_object_t reply = NULL;
uint64_t operation = xpc_dictionary_get_uint64(message, kSecXPCKeyOperation);
xpc_connection_t connection = securityd_connection_for_operation((enum SecXPCOperation)operation);
const int max_tries = SECURITYD_MAX_XPC_TRIES;
unsigned int tries_left = max_tries;
do {
if (reply) xpc_release(reply);
reply = xpc_connection_send_message_with_reply_sync(connection, message);
} while (reply == XPC_ERROR_CONNECTION_INTERRUPTED && --tries_left > 0);
_securityd_process_message_reply(&reply, error, connection, operation);
return reply;
}
static void
_securityd_message_with_reply_async_inner(xpc_object_t message,
dispatch_queue_t replyq,
securityd_handler_t handler,
uint32_t tries_left)
{
uint64_t operation = xpc_dictionary_get_uint64(message, kSecXPCKeyOperation);
xpc_connection_t connection = securityd_connection_for_operation((enum SecXPCOperation)operation);
xpc_retain(message);
dispatch_retain(replyq);
securityd_handler_t handlerCopy = Block_copy(handler);
xpc_connection_send_message_with_reply(connection, message, replyq, ^(xpc_object_t _Nonnull reply) {
if (reply == XPC_ERROR_CONNECTION_INTERRUPTED && tries_left > 0) {
_securityd_message_with_reply_async_inner(message, replyq, handlerCopy, tries_left - 1);
} else {
CFErrorRef error = NULL;
_securityd_process_message_reply(&reply, &error, connection, operation);
handlerCopy(reply, error);
CFReleaseNull(error);
}
xpc_release(message);
dispatch_release(replyq);
Block_release(handlerCopy);
});
}
void
securityd_message_with_reply_async(xpc_object_t message,
dispatch_queue_t replyq,
securityd_handler_t handler)
{
_securityd_message_with_reply_async_inner(message, replyq, handler, SECURITYD_MAX_XPC_TRIES);
}
XPC_RETURNS_RETAINED
xpc_object_t
securityd_create_message(enum SecXPCOperation op, CFErrorRef* error)
{
xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
if (message) {
xpc_dictionary_set_uint64(message, kSecXPCKeyOperation, op);
} else {
SecCFCreateError(kSecXPCErrorConnectionFailed, sSecXPCErrorDomain,
CFSTR("xpc_dictionary_create returned NULL"), NULL, error);
}
return message;
}
bool securityd_message_no_error(xpc_object_t message, CFErrorRef *error) {
xpc_object_t xpc_error = NULL;
if (message == NULL)
return false;
xpc_error = xpc_dictionary_get_value(message, kSecXPCKeyError);
if (xpc_error == NULL)
return true;
CFErrorRef localError = SecCreateCFErrorWithXPCObject(xpc_error);
#if TARGET_OS_IPHONE
secdebug("xpc", "Talking to securityd failed with error: %@", localError);
#else
#if !defined(NDEBUG)
uint64_t operation = xpc_dictionary_get_uint64(message, kSecXPCKeyOperation);
#endif
secdebug("xpc", "Talking to %s failed with error: %@",
(is_trust_operation((enum SecXPCOperation)operation)) ? "trustd" : "secd", localError);
#endif
if (error) {
*error = localError;
} else {
CFReleaseSafe(localError);
}
return false;
}
bool securityd_send_sync_and_do(enum SecXPCOperation op, CFErrorRef *error,
bool (^add_to_message)(xpc_object_t message, CFErrorRef* error),
bool (^handle_response)(xpc_object_t _Nonnull response, CFErrorRef* error)) {
xpc_object_t message = securityd_create_message(op, error);
bool ok = false;
if (message) {
if (!add_to_message || add_to_message(message, error)) {
xpc_object_t response = securityd_message_with_reply_sync(message, error);
if (response) {
if (securityd_message_no_error(response, error)) {
ok = (!handle_response || handle_response(response, error));
}
xpc_release(response);
}
}
xpc_release(message);
}
return ok;
}
void securityd_send_async_and_do(enum SecXPCOperation op, dispatch_queue_t replyq,
bool (^add_to_message)(xpc_object_t message, CFErrorRef* error),
securityd_handler_t handler) {
CFErrorRef error = NULL;
xpc_object_t message = securityd_create_message(op, &error);
if (message == NULL) {
handler(NULL, error);
CFReleaseNull(error);
return;
}
if (add_to_message != NULL) {
if (!add_to_message(message, &error)) {
handler(NULL, error);
xpc_release(message);
CFReleaseNull(error);
return;
}
}
securityd_message_with_reply_async(message, replyq, ^(xpc_object_t reply, CFErrorRef error2) {
if (error2 != NULL) {
handler(NULL, error2);
return;
}
CFErrorRef error3 = NULL;
if (!securityd_message_no_error(reply, &error3)) {
handler(NULL, error3);
CFReleaseNull(error3);
return;
}
handler(reply, NULL);
});
xpc_release(message);
}
CFDictionaryRef
_SecSecuritydCopyWhoAmI(CFErrorRef *error)
{
CFDictionaryRef reply = NULL;
xpc_object_t message = securityd_create_message(kSecXPCOpWhoAmI, error);
if (message) {
xpc_object_t response = securityd_message_with_reply_sync(message, error);
if (response) {
reply = _CFXPCCreateCFObjectFromXPCObject(response);
xpc_release(response);
} else {
secerror("Securityd failed getting whoamid with error: %@",
error ? *error : NULL);
}
xpc_release(message);
}
return reply;
}
bool
_SecSyncBubbleTransfer(CFArrayRef services, uid_t uid, CFErrorRef *error)
{
xpc_object_t message;
bool reply = false;
message = securityd_create_message(kSecXPCOpTransmogrifyToSyncBubble, error);
if (message) {
xpc_dictionary_set_int64(message, "uid", uid);
if (SecXPCDictionarySetPList(message, "services", services, error)) {
xpc_object_t response = securityd_message_with_reply_sync(message, error);
if (response) {
reply = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
if (!reply)
securityd_message_no_error(response, error);
xpc_release(response);
}
xpc_release(message);
}
}
return reply;
}
bool
_SecSystemKeychainTransfer(CFErrorRef *error)
{
xpc_object_t message;
bool reply = false;
message = securityd_create_message(kSecXPCOpTransmogrifyToSystemKeychain, error);
if (message) {
xpc_object_t response = securityd_message_with_reply_sync(message, error);
if (response) {
reply = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
if (!reply)
securityd_message_no_error(response, error);
xpc_release(response);
}
xpc_release(message);
}
return reply;
}
bool
_SecSyncDeleteUserViews(uid_t uid, CFErrorRef *error)
{
xpc_object_t message;
bool reply = false;
message = securityd_create_message(kSecXPCOpDeleteUserView, error);
if (message) {
xpc_dictionary_set_int64(message, "uid", uid);
xpc_object_t response = securityd_message_with_reply_sync(message, error);
if (response) {
reply = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
if (!reply)
securityd_message_no_error(response, error);
xpc_release(response);
}
xpc_release(message);
}
return reply;
}
XPC_RETURNS_RETAINED xpc_endpoint_t
_SecSecuritydCopyEndpoint(enum SecXPCOperation op, CFErrorRef *error)
{
xpc_endpoint_t endpoint = NULL;
xpc_object_t message = securityd_create_message(op, error);
if (message) {
xpc_object_t response = securityd_message_with_reply_sync(message, error);
if (response) {
endpoint = xpc_dictionary_get_value(response, kSecXPCKeyEndpoint);
if (endpoint) {
if(xpc_get_type(endpoint) != XPC_TYPE_ENDPOINT) {
secerror("endpoint was not an endpoint");
endpoint = NULL;
} else {
xpc_retain(endpoint);
}
} else {
secerror("endpoint was null");
}
xpc_release(response);
} else {
secerror("Securityd failed getting endpoint with error: %@", error ? *error : NULL);
}
xpc_release(message);
}
return endpoint;
}
XPC_RETURNS_RETAINED xpc_endpoint_t
_SecSecuritydCopyCKKSEndpoint(CFErrorRef *error)
{
return NULL;
}
XPC_RETURNS_RETAINED xpc_endpoint_t
_SecSecuritydCopyKeychainControlEndpoint(CFErrorRef* error)
{
return _SecSecuritydCopyEndpoint(kSecXPCOpKeychainControlEndpoint, error);
}
XPC_RETURNS_RETAINED xpc_endpoint_t
_SecSecuritydCopySFKeychainEndpoint(CFErrorRef* error)
{
return _SecSecuritydCopyEndpoint(kSecXPCOpSFKeychainEndpoint, error);
}