]> git.saurik.com Git - apple/configd.git/blobdiff - configd.tproj/session.c
configd-1109.101.1.tar.gz
[apple/configd.git] / configd.tproj / session.c
index 94e421345bf1adad31fc8c04eb147804006b39ec..2e88cc3d5cd3a2733ec1478c65389ed58af5546f 100644 (file)
@@ -1,15 +1,15 @@
 /*
- * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000, 2001, 2003-2005, 2007-2020 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
- * 
+ *
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
  * compliance with the License. Please obtain a copy of the License at
  * http://www.opensource.apple.com/apsl/ and read it before using this
  * file.
- * 
+ *
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
@@ -17,7 +17,7 @@
  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  * Please see the License for the specific language governing rights and
  * limitations under the License.
- * 
+ *
  * @APPLE_LICENSE_HEADER_END@
  */
 
  * - initial revision
  */
 
+#include <SystemConfiguration/SystemConfiguration.h>
 #include "configd.h"
 #include "configd_server.h"
+#include "pattern.h"
 #include "session.h"
 
-/* information maintained for each active session */
-static serverSessionRef        *sessions = NULL;
-static int             nSessions = 0;
+#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
 
 
 __private_extern__
 serverSessionRef
 getSession(mach_port_t server)
 {
-       int     i;
+       __block serverSessionRef        session;
 
-       if (server == MACH_PORT_NULL) {
-               SCLog(TRUE, LOG_NOTICE, CFSTR("Excuse me, why is getSession() being called with an invalid port?"));
+       assert(server != MACH_PORT_NULL);
+       dispatch_sync(sessionQueue(), ^{
+               session = (serverSessionRef)CFDictionaryGetValue(client_sessions,
+                                                                (const void *)(uintptr_t)server);
+       });
+
+       return session;
+}
+
+
+__private_extern__
+serverSessionRef
+getSessionNum(CFNumberRef serverNum)
+{
+       union {
+               mach_port_t     mp;
+               uint64_t        val;
+       } server;
+       serverSessionRef        session;
+
+       (void) CFNumberGetValue(serverNum, kCFNumberSInt64Type, &server.val);
+       session = getSession(server.mp);
+
+       return session;
+}
+
+
+__private_extern__
+serverSessionRef
+getSessionStr(CFStringRef serverKey)
+{
+       mach_port_t             server;
+       serverSessionRef        session;
+       char                    str[16];
+
+       (void) _SC_cfstring_to_cstring(serverKey, str, sizeof(str), kCFStringEncodingASCII);
+       server = atoi(str);
+       session = getSession(server);
+
+       return session;
+}
+
+
+#if __has_feature(ptrauth_intrinsics)
+extern const struct { char c; } objc_absolute_packed_isa_class_mask;
+#endif
+
+static void
+memcpy_objc_object(void* dst, const void* restrict src, size_t size)
+{
+       // first, we copy the object
+       memcpy(dst, src, size);
+
+       // then, if needed, fix the isa pointer
+  #if  __has_feature(ptrauth_intrinsics)
+       uintptr_t       flags;
+       uintptr_t       isa_mask;
+       void **         opaqueObject;
+       uintptr_t       raw_isa;
+       uintptr_t       real_isa;
+
+       opaqueObject = (void**)src;
+       isa_mask = (uintptr_t)&objc_absolute_packed_isa_class_mask;
+       flags = (uintptr_t)(*opaqueObject) & ~isa_mask;
+       real_isa = (uintptr_t)(*opaqueObject) & isa_mask;
+
+    #if                __has_feature(ptrauth_objc_isa)
+       raw_isa = (uintptr_t)ptrauth_auth_data((void *)real_isa,
+                                              ptrauth_key_process_independent_data,
+                                              ptrauth_blend_discriminator(opaqueObject, 0x6AE1));
+    #else      // __has_feature(ptrauth_objc_isa)
+       raw_isa = (uintptr_t)ptrauth_strip((void*)real_isa, ptrauth_key_process_independent_data);
+    #endif     // __has_feature(ptrauth_objc_isa)
+       ((CFRuntimeBase*)dst)->_cfisa = raw_isa;
+       ((uint64_t*)dst)[0] |= flags;
+  #endif       // __has_feature(ptrauth_intrinsics)
+}
+
+
+__private_extern__
+serverSessionRef
+tempSession(mach_port_t server, CFStringRef name, audit_token_t auditToken)
+{
+       static dispatch_once_t          once;
+       SCDynamicStorePrivateRef        storePrivate;   /* temp session */
+       static serverSession            temp_session;
+
+       dispatch_once(&once, ^{
+               memcpy_objc_object(&temp_session,       /* use "server" session clone */
+                                  server_session,
+                                  sizeof(temp_session));
+               (void) __SCDynamicStoreOpen(&temp_session.store, NULL);
+       });
+
+       if (temp_session.key != server) {
+               // if not SCDynamicStore "server" port
                return NULL;
        }
 
-       for (i = 0; i < nSessions; i++) {
-               serverSessionRef        thisSession = sessions[i];
-
-               if (thisSession == NULL) {
-                       /* found an empty slot, skip it */
-                       continue;
-               } else if (thisSession->key == server) {
-                       return thisSession;     /* we've seen this server before */
-               } else if (thisSession->store &&
-                                     (((SCDynamicStorePrivateRef)thisSession->store)->notifySignalTask == server)) {
-                       return thisSession;
-               }
+       /* save audit token, caller entitlements */
+       temp_session.auditToken                 = auditToken;
+       temp_session.callerEUID                 = 1;            /* not "root" */
+       temp_session.callerRootAccess           = UNKNOWN;
+       if ((temp_session.callerWriteEntitlement != NULL) &&
+           (temp_session.callerWriteEntitlement != kCFNull)) {
+               CFRelease(temp_session.callerWriteEntitlement);
        }
+       temp_session.callerWriteEntitlement     = kCFNull;      /* UNKNOWN */
+
+       /* save name */
+       storePrivate = (SCDynamicStorePrivateRef)temp_session.store;
+       if (storePrivate->name != NULL) CFRelease(storePrivate->name);
+       storePrivate->name = CFRetain(name);
 
-       /* no sessions available */
-       return NULL;
+       return &temp_session;
+}
+
+
+__private_extern__
+void
+addSession(serverSessionRef session, Boolean isMain)
+{
+       session->serverChannel = dispatch_mach_create_f("configd/SCDynamicStore",
+                                                       server_queue(),
+                                                       (void *)session,
+                                                       server_mach_channel_handler);
+       if (!isMain) {
+               // if not main SCDynamicStore port, watch for exit
+               dispatch_mach_notify_no_senders(session->serverChannel, FALSE);
+       }
+#if    TARGET_OS_SIMULATOR
+       // simulators don't support MiG QoS propagation yet
+       dispatch_set_qos_class_fallback(session->serverChannel, QOS_CLASS_USER_INITIATED);
+#else
+       dispatch_set_qos_class_fallback(session->serverChannel, QOS_CLASS_BACKGROUND);
+#endif
+       dispatch_mach_connect(session->serverChannel, session->key, MACH_PORT_NULL, NULL);
+       return;
 }
 
 
 __private_extern__
 serverSessionRef
-addSession(CFMachPortRef server)
+addClient(mach_port_t server, audit_token_t audit_token)
 {
-       int     i;
-       int     n = -1;
 
-       if (nSessions <= 0) {
-               /* new session (actually, the first) found */
-               sessions = malloc(sizeof(serverSessionRef));
-               n = 0;
-               nSessions = 1;
-       } else {
-               for (i = 0; i < nSessions; i++) {
-                       if (sessions[i] == NULL) {
-                               /* found an empty slot, use it */
-                               n = i;
-                       }
+       __block serverSessionRef        newSession      = NULL;
+
+       dispatch_sync(sessionQueue(), ^{
+               Boolean         ok;
+
+               // check to see if we already have an open session
+               ok = CFDictionaryContainsKey(client_sessions,
+                                            (const void *)(uintptr_t)server);
+               if (ok) {
+                       // if we've already added a session for this port
+                       return;
                }
-               /* new session identified */
-               if (n < 0) {
-                       /* no empty slots, add one to the list */
-                       n = nSessions++;
-                       sessions = reallocf(sessions, ((nSessions) * sizeof(serverSessionRef)));
+
+               // allocate a new session for "the" server
+               newSession = __serverSessionCreate(NULL, MACH_PORT_NULL);
+               if (newSession != NULL) {
+                       // and add a port --> session mapping
+                       CFDictionarySetValue(client_sessions,
+                                            (const void *)(uintptr_t)newSession->key,
+                                            newSession);
+
+                       // save the audit_token in case we need to check the callers credentials
+                       newSession->auditToken = audit_token;
+
+                       CFRelease(newSession);  // reference held by dictionary
                }
+       });
+
+       if (newSession != NULL) {
+               addSession(newSession, FALSE);
        }
 
-       // allocate a new session for this server
-       sessions[n] = malloc(sizeof(serverSession));
-       sessions[n]->key                 = CFMachPortGetPort(server);
-       sessions[n]->serverPort          = server;
-       sessions[n]->serverRunLoopSource = NULL;
-       sessions[n]->store               = NULL;
-       sessions[n]->callerEUID          = 1;           /* not "root" */
-       sessions[n]->callerEGID          = 1;           /* not "wheel" */
+       return newSession;
+}
+
 
-       return sessions[n];
+__private_extern__
+serverSessionRef
+addServer(mach_port_t server)
+{
+       // allocate a session for "the" server
+       server_session = __serverSessionCreate(NULL, server);
+       addSession(server_session, TRUE);
+
+       // add a state dump handler
+       add_state_handler();
+
+       return server_session;
 }
 
 
 __private_extern__
 void
-removeSession(mach_port_t server)
+cleanupSession(serverSessionRef session)
 {
-       int                     i;
-       serverSessionRef        thisSession;
-       CFStringRef             sessionKey;
+       mach_port_t     server          = session->key;
+
+       SC_trace("cleanup : %5d", server);
+
+       /*
+        * Close any open connections including cancelling any outstanding
+        * notification requests and releasing any locks.
+        */
+       __MACH_PORT_DEBUG(TRUE, "*** cleanupSession", server);
+       (void) __SCDynamicStoreClose(&session->store);
+       __MACH_PORT_DEBUG(TRUE, "*** cleanupSession (after __SCDynamicStoreClose)", server);
+
+       /*
+        * Our send right has already been removed. Remove our receive right.
+        */
+#ifdef HAVE_MACHPORT_GUARDS
+       (void) mach_port_destruct(mach_task_self(), server, 0, (mach_port_context_t)session);
+#else  // HAVE_MACHPORT_GUARDS
+       (void) mach_port_mod_refs(mach_task_self(), server, MACH_PORT_RIGHT_RECEIVE, -1);
+#endif // HAVE_MACHPORT_GUARDS
+
+       /*
+        * release any entitlement info
+        */
+       if ((session->callerWriteEntitlement != NULL) &&
+           (session->callerWriteEntitlement != kCFNull)) {
+               CFRelease(session->callerWriteEntitlement);
+       }
 
-       for (i = 0; i < nSessions; i++) {
-               thisSession = sessions[i];
+       /*
+        * get rid of the per-session structure.
+        */
+       dispatch_sync(sessionQueue(), ^{
+               CFDictionaryRemoveValue(client_sessions,
+                                       (const void *)(uintptr_t)server);
+       });
 
-               if (thisSession == NULL) {
-                       /* found an empty slot, skip it */
-                       continue;
-               } else if (thisSession->key == server) {
-                       /*
-                        * We don't need any remaining information in the
-                        * sessionData dictionary, remove it.
-                        */
-                       sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), server);
-                       CFDictionaryRemoveValue(sessionData, sessionKey);
-                       CFRelease(sessionKey);
+       return;
+}
 
-                       /*
-                        * Lastly, get rid of the per-session structure.
-                        */
-                       free(thisSession);
-                       sessions[i] = NULL;
 
-                       return;
-               }
+__private_extern__
+void
+closeSession(serverSessionRef session)
+{
+       /*
+        * cancel and release the mach channel
+        */
+       if (session->serverChannel != NULL) {
+               dispatch_mach_cancel(session->serverChannel);
+               dispatch_release(session->serverChannel);
+               session->serverChannel = NULL;
        }
 
        return;
 }
 
 
+typedef struct ReportSessionInfo {
+       FILE    *f;
+       int     n;
+} ReportSessionInfo, *ReportSessionInfoRef;
+
+static void
+printOne(const void *key, const void *value, void *context)
+{
+#pragma unused(key)
+       ReportSessionInfoRef    reportInfo      = (ReportSessionInfoRef)context;
+       serverSessionRef        session         = (serverSessionRef)value;
+
+       SCPrint(TRUE, reportInfo->f, CFSTR("  %d : port = 0x%x"), ++reportInfo->n, session->key);
+       SCPrint(TRUE, reportInfo->f, CFSTR(", name = %@"), session->name);
+       if (session->changedKeys != NULL) {
+               SCPrint(TRUE, reportInfo->f, CFSTR("\n    changedKeys = %@"), session->changedKeys);
+       }
+       if (session->sessionKeys != NULL) {
+               SCPrint(TRUE, reportInfo->f, CFSTR("\n    sessionKeys = %@"), session->sessionKeys);
+       }
+       SCPrint(TRUE, reportInfo->f, CFSTR("\n"));
+       return;
+}
+
+
 __private_extern__
 void
-cleanupSession(mach_port_t server)
+listSessions(FILE *f)
 {
-       int             i;
+       dispatch_sync(sessionQueue(), ^{
+               ReportSessionInfo       reportInfo      = { .f = f, .n = 0 };
+
+               SCPrint(TRUE, f, CFSTR("Current sessions :\n"));
+               CFDictionaryApplyFunction(client_sessions,
+                                         printOne,
+                                         (void *)&reportInfo);
+               SCPrint(TRUE, f, CFSTR("\n"));
+       });
+       return;
+}
 
-       for (i = 0; i < nSessions; i++) {
-               serverSessionRef        thisSession = sessions[i];
 
-               if ((thisSession != NULL) && (thisSession->key == server)) {
-                       /*
-                        * session entry still exists.
-                        */
+#include <Security/Security.h>
+#include <Security/SecTask.h>
 
-                       if (_configd_trace) {
-                               SCTrace(TRUE, _configd_trace, CFSTR("cleanup : %5d\n"), server);
+static CFTypeRef
+copyEntitlement(serverSessionRef session, CFStringRef entitlement)
+{
+       SecTaskRef      task;
+       CFTypeRef       value   = NULL;
+
+       // Create the security task from the audit token
+       task = SecTaskCreateWithAuditToken(NULL, session->auditToken);
+       if (task != NULL) {
+               CFErrorRef      error   = NULL;
+
+               // Get the value for the entitlement
+               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))) {
+                               // if unexpected error
+                               SC_log(LOG_NOTICE, "SecTaskCopyValueForEntitlement(,\"%@\",) failed, error = %@ : %@",
+                                      entitlement,
+                                      error,
+                                      session->name);
                        }
+                       CFRelease(error);
+               }
 
-                       /*
-                        * Ensure that any changes made while we held the "lock"
-                        * are discarded.
-                        */
-                       if ((storeLocked > 0) &&
-                           ((SCDynamicStorePrivateRef)thisSession->store)->locked) {
-                               /*
-                                * swap store and associated data which, after
-                                * being closed, will result in the restoration
-                                * of the original pre-"locked" data.
-                                */
-                               _swapLockedStoreData();
-                       }
+               CFRelease(task);
+       } else {
+               SC_log(LOG_NOTICE, "SecTaskCreateWithAuditToken() failed: %@",
+                      session->name);
+       }
 
-                       /*
-                        * Close any open connections including cancelling any outstanding
-                        * notification requests and releasing any locks.
-                        */
-                       (void) __SCDynamicStoreClose(&thisSession->store, TRUE);
+       return value;
+}
 
-                       /*
-                        * Lastly, remove the session entry.
-                        */
-                       removeSession(server);
 
-                       return;
-               }
+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,               // auidp
+                           NULL,               // euid
+                           NULL,               // egid
+                           NULL,               // ruid
+                           NULL,               // rgid
+                           &caller_pid,        // pid
+                           NULL,               // asid
+                           NULL);              // tid
+#endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
+
+       return caller_pid;
+}
+
+
+__private_extern__
+Boolean
+hasRootAccess(serverSessionRef session)
+{
+#if    !TARGET_OS_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,                       // auidp
+                                   &session->callerEUID,       // euid
+                                   NULL,                       // egid
+                                   NULL,                       // ruid
+                                   NULL,                       // rgid
+                                   NULL,                       // pid
+                                   NULL,                       // asid
+                                   NULL);                      // tid
+#endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
+               session->callerRootAccess = (session->callerEUID == 0) ? YES : NO;
        }
-       return;
+
+       return (session->callerRootAccess == YES) ? TRUE : FALSE;
+
+#else  // !TARGET_OS_SIMULATOR
+#pragma unused(session)
+
+       /*
+        * assume that all processes interacting with
+        * the iOS Simulator "configd" are OK.
+        */
+       return TRUE;
+
+#endif // !TARGET_OS_SIMULATOR
 }
 
 
 __private_extern__
-void
-listSessions()
+Boolean
+hasWriteAccess(serverSessionRef session, const char *op, CFStringRef key)
 {
-       int     i;
+       Boolean isSetup;
+
+       // need to special case writing "Setup:" keys
+       isSetup = CFStringHasPrefix(key, kSCDynamicStoreDomainSetup);
 
-       fprintf(stderr, "Current sessions:");
-       for (i = 0; i < nSessions; i++) {
-               serverSessionRef        thisSession = sessions[i];
+       if (hasRootAccess(session)) {
+               pid_t   pid;
 
-               if (thisSession == NULL) {
-                       continue;
+               // grant write access to eUID==0 processes
+
+               pid = sessionPid(session);
+               if (isSetup && (pid != getpid())) {
+                       /*
+                        * WAIT!!!
+                        *
+                        * This is NOT configd (or a plugin) trying to
+                        * write to an SCDynamicStore "Setup:" key.  In
+                        * general, this is unwise and we should at the
+                        * very least complain.
+                        */
+                       SC_log(LOG_NOTICE, "*** Non-configd process (pid=%d) attempting to %s \"%@\" ***",
+                              pid,
+                              op,
+                              key);
                }
 
-               fprintf(stderr, " %d", thisSession->key);
+               return TRUE;
+       }
+
+       if (isSetup) {
+               /*
+                * STOP!!!
+                *
+                * This is a non-root process trying to write to
+                * an SCDynamicStore "Setup:" key.  This is not
+                * something we should ever allow (regardless of
+                * any entitlements).
+                */
+               SC_log(LOG_NOTICE, "*** Non-root process (pid=%d) attempting to modify \"%@\" ***",
+                      sessionPid(session),
+                      key);
+
+               return FALSE;
+       }
+
+       if (session->callerWriteEntitlement == kCFNull) {
+               session->callerWriteEntitlement = copyEntitlement(session,
+                                                                 kSCWriteEntitlementName);
+       }
+
+       if (session->callerWriteEntitlement == NULL) {
+               return FALSE;
+       }
+
+       if (isA_CFBoolean(session->callerWriteEntitlement) &&
+           CFBooleanGetValue(session->callerWriteEntitlement)) {
+               // grant write access to "entitled" processes
+               return TRUE;
+       }
 
-               if (thisSession->store) {
-                       task_t  task = ((SCDynamicStorePrivateRef)thisSession->store)->notifySignalTask;
+       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)) {
+                               // if key matches one of the entitlement "keys", grant
+                               // write access
+                               return TRUE;
+                       }
+               }
 
-                       if (task != TASK_NULL) {
-                              fprintf(stderr, "/%d", task);
+               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)) {
+                                               // if key matches one of the entitlement
+                                               // "patterns", grant write access
+                                               return TRUE;
+                                       }
+                               }
                        }
                }
        }
-       fprintf(stderr, "\n");
+
+       return FALSE;
 }
 
+
+__private_extern__
+Boolean
+hasPathAccess(serverSessionRef session, const char *path)
+{
+       pid_t   pid;
+       char    realPath[PATH_MAX];
+
+       if (realpath(path, realPath) == NULL) {
+               SC_log(LOG_INFO, "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,               // auidp
+                           NULL,               // euid
+                           NULL,               // egid
+                           NULL,               // ruid
+                           NULL,               // rgid
+                           &pid,               // pid
+                           NULL,               // asid
+                           NULL);              // tid
+#endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE
+       if (sandbox_check(pid,                                                  // pid
+                         "file-write-data",                                    // operation
+                         SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT,        // sandbox_filter_type
+                         realPath) > 0) {                                      // ...
+               SC_log(LOG_INFO, "sandbox access denied: %s", strerror(errno));
+               return FALSE;
+       }
+
+       return TRUE;
+}