#include <SystemConfiguration/SystemConfiguration.h>
#include "configd.h"
#include "configd_server.h"
#include "pattern.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;
__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;
#if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
if ((temp_session->callerWriteEntitlement != NULL) &&
(temp_session->callerWriteEntitlement != kCFNull)) {
CFRelease(temp_session->callerWriteEntitlement);
}
temp_session->callerWriteEntitlement = kCFNull;
#endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
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);
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;
#if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
sessions[n]->callerWriteEntitlement = kCFNull;
#endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
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);
#if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
if ((thisSession->callerWriteEntitlement != NULL) &&
(thisSession->callerWriteEntitlement != kCFNull)) {
CFRelease(thisSession->callerWriteEntitlement);
}
#endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
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 >= 1080)
#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 CFTypeRef
copyEntitlement(serverSessionRef session, CFStringRef entitlement)
{
SecTaskRef task;
CFTypeRef value = NULL;
task = SecTaskCreateWithAuditToken(NULL, session->auditToken);
if (task != NULL) {
CFErrorRef error = NULL;
value = SecTaskCopyValueForEntitlement(task, entitlement, &error);
if ((value == NULL) && (error != NULL)) {
CFIndex code = CFErrorGetCode(error);
CFStringRef domain = CFErrorGetDomain(error);
if (!CFEqual(domain, kCFErrorDomainMach) ||
((code != kIOReturnInvalid) && (code != kIOReturnNotFound))) {
SCLog(TRUE, LOG_ERR,
CFSTR("SecTaskCopyValueForEntitlement(,\"%@\",) failed, error = %@ : %@"),
entitlement,
error,
sessionName(session));
}
CFRelease(error);
}
CFRelease(task);
} else {
SCLog(TRUE, LOG_ERR,
CFSTR("SecTaskCreateWithAuditToken() failed: %@"),
sessionName(session));
}
return value;
}
#endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
static pid_t
sessionPid(serverSessionRef session)
{
pid_t caller_pid;
#if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
caller_pid = audit_token_to_pid(session->auditToken);
#else // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
audit_token_to_au32(session->auditToken,
NULL, NULL, NULL, NULL, NULL, &caller_pid, NULL, NULL); #endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
return caller_pid;
}
__private_extern__
Boolean
hasRootAccess(serverSessionRef session)
{
#if !TARGET_IPHONE_SIMULATOR
if (session->callerRootAccess == UNKNOWN) {
#if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
session->callerEUID = audit_token_to_euid(session->auditToken);
#else // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
audit_token_to_au32(session->auditToken,
NULL, &session->callerEUID, NULL, NULL, NULL, NULL, NULL, NULL); #endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
session->callerRootAccess = (session->callerEUID == 0) ? YES : NO;
}
return (session->callerRootAccess == YES) ? TRUE : FALSE;
#else // !TARGET_IPHONE_SIMULATOR
return TRUE;
#endif // !TARGET_IPHONE_SIMULATOR
}
__private_extern__
Boolean
hasWriteAccess(serverSessionRef session, CFStringRef key)
{
Boolean isSetup;
isSetup = CFStringHasPrefix(key, kSCDynamicStoreDomainSetup);
if (hasRootAccess(session)) {
pid_t pid;
pid = sessionPid(session);
if (isSetup && (pid != getpid())) {
SCLog(TRUE, LOG_ERR,
CFSTR("*** Non-configd process (pid=%d) attempting to modify \"%@\" ***"),
pid,
key);
}
return TRUE;
}
if (isSetup) {
SCLog(TRUE, LOG_ERR,
CFSTR("*** Non-root process (pid=%d) attempting to modify \"%@\" ***"),
sessionPid(session),
key);
}
#if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
if (session->callerWriteEntitlement == kCFNull) {
session->callerWriteEntitlement = copyEntitlement(session,
kSCWriteEntitlementName);
}
if (session->callerWriteEntitlement == NULL) {
return FALSE;
}
if (isA_CFBoolean(session->callerWriteEntitlement) &&
CFBooleanGetValue(session->callerWriteEntitlement)) {
return TRUE;
}
if (isA_CFDictionary(session->callerWriteEntitlement)) {
CFArrayRef keys;
CFArrayRef patterns;
keys = CFDictionaryGetValue(session->callerWriteEntitlement, CFSTR("keys"));
if (isA_CFArray(keys)) {
if (CFArrayContainsValue(keys,
CFRangeMake(0, CFArrayGetCount(keys)),
key)) {
return TRUE;
}
}
patterns = CFDictionaryGetValue(session->callerWriteEntitlement, CFSTR("patterns"));
if (isA_CFArray(patterns)) {
CFIndex i;
CFIndex n = CFArrayGetCount(patterns);
for (i = 0; i < n; i++) {
CFStringRef pattern;
pattern = CFArrayGetValueAtIndex(patterns, i);
if (isA_CFString(pattern)) {
if (patternKeyMatches(pattern, key)) {
return TRUE;
}
}
}
}
}
#endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
return 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;
}
#if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
pid = audit_token_to_pid(session->auditToken);
#else // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
audit_token_to_au32(session->auditToken,
NULL, NULL, NULL, NULL, NULL, &pid, NULL, NULL); #endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
if (sandbox_check(pid, "file-write-data", SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, realPath) > 0) { SCLog(TRUE, LOG_DEBUG, CFSTR("hasPathAccess sandbox access denied: %s"), strerror(errno));
return FALSE;
}
return TRUE;
}