+#include <unistd.h>
+#include <bsm/libbsm.h>
+#include <os/state_private.h>
+#include <sandbox.h>
+
+#if !TARGET_OS_SIMULATOR || (defined(IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED) && (IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED >= 1090))
+#define HAVE_MACHPORT_GUARDS
+#endif
+
+
+/* information maintained for the main listener */
+static serverSessionRef server_session = NULL;
+
+/*
+ * information maintained for each active session
+ * Note: sync w/sessionQueue()
+ */
+static CFMutableDictionaryRef client_sessions = NULL;
+static CFIndex client_sessions_advise = 250; // when snapshot handler should detail sessions
+
+
+static dispatch_queue_t
+sessionQueue(void)
+{
+ static dispatch_once_t once;
+ static dispatch_queue_t q;
+
+ dispatch_once(&once, ^{
+ // allocate mapping between [client] session mach port and session info
+ client_sessions = CFDictionaryCreateMutable(NULL,
+ 0,
+ NULL, // use the actual mach_port_t as the key
+ &kCFTypeDictionaryValueCallBacks);
+
+ // and a queue to synchronize access to the mapping
+ q = dispatch_queue_create("SCDynamicStore/sessions", NULL);
+ });
+
+ return q;
+}
+
+
+#pragma mark -
+#pragma mark __serverSession object
+
+static CFStringRef __serverSessionCopyDescription (CFTypeRef cf);
+static void __serverSessionDeallocate (CFTypeRef cf);
+
+static const CFRuntimeClass __serverSessionClass = {
+ 0, // version
+ "serverSession", // className
+ NULL, // init
+ NULL, // copy
+ __serverSessionDeallocate, // dealloc
+ NULL, // equal
+ NULL, // hash
+ NULL, // copyFormattingDesc
+ __serverSessionCopyDescription // copyDebugDesc
+};
+
+static CFTypeID __serverSessionTypeID = _kCFRuntimeNotATypeID;
+
+
+static CFStringRef
+__serverSessionCopyDescription(CFTypeRef cf)
+{
+ CFAllocatorRef allocator = CFGetAllocator(cf);
+ CFMutableStringRef result;
+ serverSessionRef session = (serverSessionRef)cf;
+
+ result = CFStringCreateMutable(allocator, 0);
+ CFStringAppendFormat(result, NULL, CFSTR("<serverSession %p [%p]> {"), cf, allocator);
+
+ // add client port
+ CFStringAppendFormat(result, NULL, CFSTR("port = 0x%x (%d)"), session->key, session->key);
+
+ // add session info
+ if (session->name != NULL) {
+ CFStringAppendFormat(result, NULL, CFSTR(", name = %@"), session->name);
+ }
+
+ CFStringAppendFormat(result, NULL, CFSTR("}"));
+ return result;
+}
+
+
+static void
+__serverSessionDeallocate(CFTypeRef cf)
+{
+#pragma unused(cf)
+ serverSessionRef session = (serverSessionRef)cf;
+
+ if (session->changedKeys != NULL) CFRelease(session->changedKeys);
+ if (session->name != NULL) CFRelease(session->name);
+ if (session->sessionKeys != NULL) CFRelease(session->sessionKeys);
+
+ return;
+}
+
+
+static serverSessionRef
+__serverSessionCreate(CFAllocatorRef allocator, mach_port_t server)
+{
+ static dispatch_once_t once;
+ serverSessionRef session;
+ uint32_t size;
+
+ // initialize runtime
+ dispatch_once(&once, ^{
+ __serverSessionTypeID = _CFRuntimeRegisterClass(&__serverSessionClass);
+ });
+
+ // allocate session
+ size = sizeof(serverSession) - sizeof(CFRuntimeBase);
+ session = (serverSessionRef)_CFRuntimeCreateInstance(allocator,
+ __serverSessionTypeID,
+ size,
+ NULL);
+ if (session == NULL) {
+ return NULL;
+ }
+
+ // if needed, allocate a mach port for SCDynamicStore client
+ if (server == MACH_PORT_NULL) {
+ kern_return_t kr;
+ mach_port_t mp = MACH_PORT_NULL;
+#ifdef HAVE_MACHPORT_GUARDS
+ mach_port_options_t opts;
+#endif // HAVE_MACHPORT_GUARDS
+
+ retry_allocate :
+
+#ifdef HAVE_MACHPORT_GUARDS
+ memset(&opts, 0, sizeof(opts));
+ opts.flags = MPO_CONTEXT_AS_GUARD;
+
+ kr = mach_port_construct(mach_task_self(), &opts, (mach_port_context_t)session, &mp);
+#else // HAVE_MACHPORT_GUARDS
+ kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
+#endif // HAVE_MACHPORT_GUARDS
+
+ if (kr != KERN_SUCCESS) {
+ char *err = NULL;
+
+ SC_log(LOG_NOTICE, "could not allocate mach port: %s", mach_error_string(kr));
+ if ((kr == KERN_NO_SPACE) || (kr == KERN_RESOURCE_SHORTAGE)) {
+ sleep(1);
+ goto retry_allocate;
+ }
+
+ (void) asprintf(&err, "Could not allocate mach port: %s", mach_error_string(kr));
+ _SC_crash(err != NULL ? err : "Could not allocate new session (mach) port",
+ NULL,
+ NULL);
+ if (err != NULL) free(err);
+ CFRelease(session);
+ return NULL;
+ }
+
+ // insert send right that will be moved to the client
+ kr = mach_port_insert_right(mach_task_self(),
+ mp,
+ mp,
+ MACH_MSG_TYPE_MAKE_SEND);
+ if (kr != KERN_SUCCESS) {
+ /*
+ * We can't insert a send right into our own port! This should
+ * only happen if someone stomped on OUR port (so let's leave
+ * the port alone).
+ */
+ SC_log(LOG_ERR, "mach_port_insert_right() failed: %s", mach_error_string(kr));
+ CFRelease(session);
+ return NULL;
+ }
+
+ server = mp;
+ }
+
+ session->callerEUID = 1; /* not "root" */
+ session->callerRootAccess = UNKNOWN;
+ session->callerWriteEntitlement = kCFNull; /* UNKNOWN */
+ session->key = server;
+// session->store = NULL;
+
+ return session;
+}
+
+
+#pragma mark -
+#pragma mark SCDynamicStore state handler
+
+
+static void
+addSessionReference(const void *key, const void *value, void *context)
+{
+#pragma unused(key)
+ CFMutableDictionaryRef dict = (CFMutableDictionaryRef)context;
+ serverSessionRef session = (serverSessionRef)value;
+
+ if (session->name != NULL) {
+ int cnt;
+ CFNumberRef num;
+
+ if (!CFDictionaryGetValueIfPresent(dict,
+ session->name,
+ (const void **)&num) ||
+ !CFNumberGetValue(num, kCFNumberIntType, &cnt)) {
+ // if first session
+ cnt = 0;
+ }
+ cnt++;
+ num = CFNumberCreate(NULL, kCFNumberIntType, &cnt);
+ CFDictionarySetValue(dict, session->name, num);
+ CFRelease(num);
+ }
+
+ return;
+}
+
+
+static void
+add_state_handler()
+{
+ os_state_block_t state_block;
+
+ state_block = ^os_state_data_t(os_state_hints_t hints) {
+#pragma unused(hints)
+ CFDataRef data = NULL;
+ CFIndex n;
+ Boolean ok;
+ os_state_data_t state_data;
+ size_t state_data_size;
+ CFIndex state_len;
+
+ n = CFDictionaryGetCount(client_sessions);
+ if (n < client_sessions_advise) {
+ CFStringRef str;
+
+ str = CFStringCreateWithFormat(NULL, NULL, CFSTR("n = %ld"), n);
+ ok = _SCSerialize(str, &data, NULL, NULL);
+ CFRelease(str);
+ } else {
+ CFMutableDictionaryRef dict;
+
+ dict = CFDictionaryCreateMutable(NULL,
+ 0,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ CFDictionaryApplyFunction(client_sessions, addSessionReference, dict);
+ ok = _SCSerialize(dict, &data, NULL, NULL);
+ CFRelease(dict);
+ }
+
+ state_len = (ok && (data != NULL)) ? CFDataGetLength(data) : 0;
+ state_data_size = OS_STATE_DATA_SIZE_NEEDED(state_len);
+ if (state_data_size > MAX_STATEDUMP_SIZE) {
+ SC_log(LOG_ERR, "SCDynamicStore/sessions : state data too large (%zd > %zd)",
+ state_data_size,
+ (size_t)MAX_STATEDUMP_SIZE);
+ if (data != NULL) CFRelease(data);
+ return NULL;
+ }
+
+ state_data = calloc(1, state_data_size);
+ if (state_data == NULL) {
+ SC_log(LOG_ERR, "SCDynamicStore/sessions: could not allocate state data");
+ if (data != NULL) CFRelease(data);
+ return NULL;
+ }
+
+ state_data->osd_type = OS_STATE_DATA_SERIALIZED_NSCF_OBJECT;
+ state_data->osd_data_size = (uint32_t)state_len;
+ strlcpy(state_data->osd_title, "SCDynamicStore/sessions", sizeof(state_data->osd_title));
+ if (state_len > 0) {
+ memcpy(state_data->osd_data, CFDataGetBytePtr(data), state_len);
+ }
+ if (data != NULL) CFRelease(data);
+
+ return state_data;
+ };
+
+ (void) os_state_add_handler(sessionQueue(), state_block);
+ return;
+}
+
+
+#pragma mark -
+#pragma mark SCDynamicStore session management