#include "configd.h"
#include "configd_server.h"
#include "session.h"
#include <unistd.h>
#include <bsm/libbsm.h>
#include <sandbox.h>
static serverSessionRef *sessions = NULL;
static int nSessions = 0;
static int lastSession = -1;
static CFRunLoopRef sessionRunLoop = NULL;
static serverSessionRef temp_session = NULL;
static void
CFMachPortInvalidateSessionCallback(CFMachPortRef port, void *info)
{
CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent();
if (!_SC_CFEqual(currentRunLoop, sessionRunLoop)) {
_SC_crash("SCDynamicStore CFMachPort invalidation error",
CFSTR("CFMachPort invalidated"),
CFSTR("An SCDynamicStore CFMachPort has incorrectly been invalidated."));
}
}
__private_extern__
serverSessionRef
getSession(mach_port_t server)
{
int i;
if (server == MACH_PORT_NULL) {
SCLog(TRUE, LOG_ERR, CFSTR("Excuse me, why is getSession() being called with an invalid port?"));
return NULL;
}
for (i = 1; i <= lastSession; i++) {
serverSessionRef thisSession = sessions[i];
if (thisSession == NULL) {
continue;
}
if (thisSession->key == server) {
return thisSession;
}
if ((thisSession->store != NULL) &&
(((SCDynamicStorePrivateRef)thisSession->store)->notifySignalTask == server)) {
return thisSession;
}
}
return NULL;
}
__private_extern__
serverSessionRef
tempSession(mach_port_t server, CFStringRef name, audit_token_t auditToken)
{
static dispatch_once_t once;
SCDynamicStorePrivateRef storePrivate;
if (sessions[0]->key != server) {
return NULL;
}
dispatch_once(&once, ^{
temp_session = sessions[0];
(void) __SCDynamicStoreOpen(&temp_session->store, NULL);
});
temp_session->auditToken = auditToken;
temp_session->callerEUID = -1;
temp_session->callerRootAccess = UNKNOWN;
temp_session->callerWriteAccess = UNKNOWN;
storePrivate = (SCDynamicStorePrivateRef)temp_session->store;
if (storePrivate->name != NULL) CFRelease(storePrivate->name);
storePrivate->name = CFRetain(name);
return temp_session;
}
__private_extern__
serverSessionRef
addSession(mach_port_t server, CFStringRef (*copyDescription)(const void *info))
{
CFMachPortContext context = { 0, NULL, NULL, NULL, NULL };
mach_port_t mp = server;
int n = -1;
if (sessionRunLoop == NULL) {
sessionRunLoop = CFRunLoopGetCurrent();
}
if (nSessions <= 0) {
n = 0;
lastSession = 0;
nSessions = 64;
sessions = malloc(nSessions * sizeof(serverSessionRef));
} else {
int i;
for (i = 1; i <= lastSession; i++) {
serverSessionRef thisSession = sessions[i];
if (thisSession == NULL) {
if (n < 0) {
n = i;
}
continue;
}
if (thisSession->key == server) {
return NULL;
}
if ((thisSession->store != NULL) &&
(((SCDynamicStorePrivateRef)thisSession->store)->notifySignalTask == server)) {
return NULL;
}
}
if (n < 0) {
n = ++lastSession;
if (lastSession >= nSessions) {
nSessions *= 2;
sessions = reallocf(sessions, (nSessions * sizeof(serverSessionRef)));
}
}
mp = MACH_PORT_NULL;
(void) mach_port_allocate(mach_task_self(),
MACH_PORT_RIGHT_RECEIVE,
&mp);
}
sessions[n] = malloc(sizeof(serverSession));
bzero(sessions[n], sizeof(serverSession));
context.info = sessions[n];
context.copyDescription = copyDescription;
sessions[n]->serverPort = _SC_CFMachPortCreateWithPort("SCDynamicStore/session",
mp,
configdCallback,
&context);
CFMachPortSetInvalidationCallBack(sessions[n]->serverPort,
CFMachPortInvalidateSessionCallback);
if (n > 0) {
(void) mach_port_insert_right(mach_task_self(),
mp,
mp,
MACH_MSG_TYPE_MAKE_SEND);
}
sessions[n]->key = mp;
sessions[n]->callerEUID = 1;
sessions[n]->callerRootAccess = UNKNOWN;
sessions[n]->callerWriteAccess = UNKNOWN;
return sessions[n];
}
__private_extern__
void
cleanupSession(mach_port_t server)
{
int i;
for (i = 1; i <= lastSession; i++) {
CFStringRef sessionKey;
serverSessionRef thisSession = sessions[i];
if (thisSession == NULL) {
continue;
}
if (thisSession->key == server) {
if (_configd_trace) {
SCTrace(TRUE, _configd_trace, CFSTR("cleanup : %5d\n"), server);
}
__MACH_PORT_DEBUG(TRUE, "*** cleanupSession", server);
(void) __SCDynamicStoreClose(&thisSession->store);
__MACH_PORT_DEBUG(TRUE, "*** cleanupSession (after __SCDynamicStoreClose)", server);
(void) mach_port_mod_refs(mach_task_self(), server, MACH_PORT_RIGHT_RECEIVE, -1);
sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), server);
CFDictionaryRemoveValue(sessionData, sessionKey);
CFRelease(sessionKey);
free(thisSession);
sessions[i] = NULL;
if (i == lastSession) {
while (--lastSession > 0) {
if (sessions[lastSession] != NULL) {
break;
}
}
}
return;
}
}
SCLog(TRUE, LOG_ERR, CFSTR("MACH_NOTIFY_NO_SENDERS w/no session, port = %d"), server);
__MACH_PORT_DEBUG(TRUE, "*** cleanupSession w/no session", server);
return;
}
__private_extern__
void
listSessions(FILE *f)
{
int i;
SCPrint(TRUE, f, CFSTR("Current sessions :\n"));
for (i = 0; i <= lastSession; i++) {
serverSessionRef thisSession = sessions[i];
if (thisSession == NULL) {
continue;
}
SCPrint(TRUE, f, CFSTR("\t%d : port = 0x%x"), i, thisSession->key);
if (thisSession->store != NULL) {
SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)thisSession->store;
if (storePrivate->notifySignalTask != TASK_NULL) {
SCPrint(TRUE, f, CFSTR(", task = %d"), storePrivate->notifySignalTask);
}
}
if (sessionData != NULL) {
CFDictionaryRef info;
CFStringRef key;
key = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), thisSession->key);
info = CFDictionaryGetValue(sessionData, key);
CFRelease(key);
if (info != NULL) {
CFStringRef name;
name = CFDictionaryGetValue(info, kSCDName);
if (name != NULL) {
SCPrint(TRUE, f, CFSTR(", name = %@"), name);
}
}
}
if (thisSession->serverPort != NULL) {
SCPrint(TRUE, f, CFSTR("\n\t\t%@"), thisSession->serverPort);
}
if (thisSession->serverRunLoopSource != NULL) {
SCPrint(TRUE, f, CFSTR("\n\t\t%@"), thisSession->serverRunLoopSource);
}
SCPrint(TRUE, f, CFSTR("\n"));
}
SCPrint(TRUE, f, CFSTR("\n"));
return;
}
#if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
#include <Security/Security.h>
#include <Security/SecTask.h>
static CFStringRef
sessionName(serverSessionRef session)
{
CFDictionaryRef info;
CFStringRef name = NULL;
CFStringRef sessionKey;
sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), session->key);
info = CFDictionaryGetValue(sessionData, sessionKey);
CFRelease(sessionKey);
if (info != NULL) {
name = CFDictionaryGetValue(info, kSCDName);
}
return (name != NULL) ? name : CFSTR("???");
}
static Boolean
hasEntitlement(serverSessionRef session, CFStringRef entitlement)
{
Boolean hasEntitlement = FALSE;
SecTaskRef task;
task = SecTaskCreateWithAuditToken(NULL, session->auditToken);
if (task != NULL) {
CFErrorRef error = NULL;
CFTypeRef value;
value = SecTaskCopyValueForEntitlement(task, entitlement, &error);
if (value != NULL) {
if (isA_CFBoolean(value)) {
if (CFBooleanGetValue(value)) {
hasEntitlement = TRUE;
}
} else {
SCLog(TRUE, LOG_ERR,
CFSTR("hasEntitlement: entitlement not valid: %@"),
sessionName(session));
}
CFRelease(value);
}
if (error != NULL) {
SCLog(TRUE,
(value == NULL) ? LOG_ERR : LOG_DEBUG,
CFSTR("hasEntitlement SecTaskCopyValueForEntitlement() %s, error domain=%@, error code=%lx"),
(value == NULL) ? "failed" : "warned",
CFErrorGetDomain(error),
CFErrorGetCode(error));
CFRelease(error);
}
CFRelease(task);
} else {
SCLog(TRUE, LOG_ERR,
CFSTR("hasEntitlement SecTaskCreateWithAuditToken() failed: %@"),
sessionName(session));
}
return hasEntitlement;
}
#endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
__private_extern__
Boolean
hasRootAccess(serverSessionRef session)
{
if (session->callerRootAccess == UNKNOWN) {
audit_token_to_au32(session->auditToken,
NULL, &session->callerEUID, NULL, NULL, NULL, NULL, NULL, NULL);
session->callerRootAccess = (session->callerEUID == 0) ? YES : NO;
}
return (session->callerRootAccess == YES) ? TRUE : FALSE;
}
__private_extern__
Boolean
hasWriteAccess(serverSessionRef session)
{
if (session->callerWriteAccess == UNKNOWN) {
session->callerWriteAccess = NO;
if (hasRootAccess(session)) {
session->callerWriteAccess = YES;
}
#if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
else if (hasEntitlement(session, kSCWriteEntitlementName)) {
session->callerWriteAccess = YES;
}
#endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
}
return (session->callerWriteAccess == YES) ? TRUE : FALSE;
}
__private_extern__
Boolean
hasPathAccess(serverSessionRef session, const char *path)
{
pid_t pid;
char realPath[PATH_MAX];
if (realpath(path, realPath) == NULL) {
SCLog(TRUE, LOG_DEBUG, CFSTR("hasPathAccess realpath() failed: %s"), strerror(errno));
return FALSE;
}
audit_token_to_au32(session->auditToken,
NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL);
if (sandbox_check(pid, "file-write-data", SANDBOX_FILTER_PATH, realPath) > 0) {
SCLog(TRUE, LOG_DEBUG, CFSTR("hasPathAccess sandbox access denied: %s"), strerror(errno));
return FALSE;
}
return TRUE;
}