]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurityd/lib/eventlistener.cpp
Security-57740.51.3.tar.gz
[apple/security.git] / OSX / libsecurityd / lib / eventlistener.cpp
index 93a3c93f4b66036e5231b64f0f4a127c6c8c1e46..65cebe8ff550e9adb9ad3be122e5584d5994ec3f 100644 (file)
@@ -29,6 +29,7 @@
 #include <notify.h>
 #include "sscommon.h"
 #include <sys/syslog.h>
+#include <Security/SecBasePriv.h>
 
 using namespace MachPlusPlus;
 
@@ -45,7 +46,7 @@ static const char* GetNotificationName ()
        const char* name = getenv (SECURITYSERVER_BOOTSTRAP_ENV);
        if (name == NULL)
        {
-               name = SECURITY_MESSAGES_NAME;
+               name = SharedMemoryCommon::kDefaultSecurityMessagesName;
        }
        
        return name;
@@ -65,8 +66,9 @@ public:
 
 
 
-SharedMemoryClientMaker::SharedMemoryClientMaker () : mClient (GetNotificationName (), kSharedMemoryPoolSize)
+SharedMemoryClientMaker::SharedMemoryClientMaker () : mClient (GetNotificationName (), kSharedMemoryPoolSize, SharedMemoryCommon::fixUID(getuid()))
 {
+    secdebug("MDSPRIVACY","[%03d] SharedMemoryClientMaker uid: %d, euid: %d, name: %s", mClient.getUID(), getuid(), geteuid(), GetNotificationName ());
 }
 
 
@@ -82,215 +84,133 @@ ModuleNexus<EventListenerList> gEventListeners;
 ModuleNexus<Mutex> gNotificationLock;
 ModuleNexus<SharedMemoryClientMaker> gMemoryClient;
 
-class NotificationPort : public MachPlusPlus::CFAutoPort
-{
-protected:
-       SharedMemoryClient *mClient;
-
-    void ReceiveImplementation(u_int8_t* buffer, SegmentOffsetType length, UnavailableReason ur);
-    static void HandleRunLoopTimer(CFRunLoopTimerRef timer, void* info);
-
-public:
-       NotificationPort (mach_port_t port);
-       virtual ~NotificationPort ();
-       virtual void receive(const MachPlusPlus::Message &msg);
-};
-
-NotificationPort::NotificationPort (mach_port_t mp) : CFAutoPort (mp)
-{
-       mClient = gMemoryClient ().Client ();
-}
-
-
-
-NotificationPort::~NotificationPort ()
-{
-}
-
+//
+// Note that once we start notifications, we want receive them forever. Don't have a cancel option.
+//
+static bool InitializeNotifications () {
+    bool initializationComplete = false;
+    static dispatch_queue_t notification_queue = EventListener::getNotificationQueue();
 
+    secdebug("MDSPRIVACY","EventListener Init: uid: %d, euid: %d, name: %s", getuid(), geteuid(), GetNotificationName ());
+    // Initialize the memory client
+    gMemoryClient();
 
-void NotificationPort::ReceiveImplementation(u_int8_t* buffer, SegmentOffsetType length, UnavailableReason ur)
-{
-    EventListenerList& eventList = gEventListeners();
-
-       // route the message to its destination
-    u_int32_t* ptr = (u_int32_t*) buffer;
-    
-    // we have a message, do the semantics...
-    SecurityServer::NotificationDomain domain = (SecurityServer::NotificationDomain) OSSwapBigToHostInt32 (*ptr++);
-    SecurityServer::NotificationEvent event = (SecurityServer::NotificationEvent) OSSwapBigToHostInt32 (*ptr++);
-    CssmData data ((u_int8_t*) ptr, buffer + length - (u_int8_t*) ptr);
-
-    EventListenerList::iterator it = eventList.begin ();
-    while (it != eventList.end ())
-    {
-        try
-        {
-            EventPointer ep = *it++;
-            if (ep->GetDomain () == domain &&
-                (ep->GetMask () & (1 << event)) != 0)
-            {
-                ep->consume (domain, event, data);
-            }
-        }
-        catch (CssmError &e)
-        {
-            if (e.error != CSSM_ERRCODE_INTERNAL_ERROR)
-            {
-                throw;
-            }
-        }
+    if (gMemoryClient().Client()->uninitialized()) {
+        secdebug("MDSPRIVACY","[%03d] FATAL: InitializeNotifications EventListener uninitialized; process will never get keychain notifications", getuid());
+        return initializationComplete;
     }
-}
-
-
+    int out_token;
 
-typedef void (^NotificationBlock)();
-
-
-
-void NotificationPort::HandleRunLoopTimer(CFRunLoopTimerRef timer, void* info)
-{
-    // reconstruct our context and call it
-    NotificationBlock nb = (NotificationBlock) info;
-    nb();
-    
-    // clean up
-    Block_release(nb);
-    CFRunLoopTimerInvalidate(timer);
-    CFRelease(timer);
-}
+    notify_handler_t receive = ^(int token){
+        try {
+            SegmentOffsetType length;
+            UnavailableReason ur;
 
+            bool result;
 
-
-void NotificationPort::receive (const MachPlusPlus::Message &msg)
-{
-    /*
-        Read each notification received and post a timer for each with an expiration of
-        zero.  I'd prefer to use a notification here, but I can't because, according to
-        the documentation, each application may only have one notification center and
-        the main application should have the right to pick the one it needs.
-    */
-    
-    SegmentOffsetType length;
-    UnavailableReason ur;
-
-    bool result;
-
-    while (true)
-    {
-        u_int8_t *buffer = new u_int8_t[kSharedMemoryPoolSize];
-    
-       {
-            StLock<Mutex> lock (gNotificationLock ());
-            result = mClient->ReadMessage(buffer, length, ur);
-            if (!result)
+            // Trust the memory client to break our loop here
+            while (true)
             {
+                u_int8_t *buffer = new u_int8_t[kSharedMemoryPoolSize];
+                {
+                    StLock<Mutex> lock (gNotificationLock ());
+                    result = gMemoryClient().Client()->ReadMessage(buffer, length, ur);
+                    if (!result)
+                    {
+                        secdebug("MDSPRIVACY","[%03d] notify_handler ReadMessage ur: %d", getuid(), ur);
+                        delete [] buffer;
+                        return;
+                    }
+                }
+
+                // Send this event off to the listeners
+                {
+                    EventListenerList& eventList = gEventListeners();
+
+                    // route the message to its destination
+                    u_int32_t* ptr = (u_int32_t*) buffer;
+
+                    // we have a message, do the semantics...
+                    SecurityServer::NotificationDomain domain = (SecurityServer::NotificationDomain) OSSwapBigToHostInt32 (*ptr++);
+                    SecurityServer::NotificationEvent event = (SecurityServer::NotificationEvent) OSSwapBigToHostInt32 (*ptr++);
+                    CssmData data ((u_int8_t*) ptr, buffer + length - (u_int8_t*) ptr);
+
+                    string descrip = SharedMemoryCommon::notificationDescription(domain, event);
+                    secdebug("MDSPRIVACY","[%03d] notify_handler: %s", getuid(), descrip.c_str());
+                    EventListenerList::iterator it = eventList.begin ();
+                    while (it != eventList.end ())
+                    {
+                        try
+                        {
+                            EventPointer ep = *it++;
+                            if (ep->GetDomain () == domain &&
+                                    (ep->GetMask () & (1 << event)) != 0)
+                            {
+                                ep->consume (domain, event, data);
+                            }
+                        }
+                        catch (CssmError &e)
+                        {
+                            // If we throw, libnotify will abort the process. Log these...
+                            secerror("caught CssmError while processing notification: %d %s", e.error, cssmErrorString(e.error));
+                        }
+                    }
+                }
+
                 delete [] buffer;
-                return;
             }
         }
-
-        // make a block that contains our data
-        NotificationBlock nb =
-            ^{
-                ReceiveImplementation(buffer, length, ur);
-                delete [] buffer;
-            };
-        
-        // keep it in scope
-        nb = Block_copy(nb);
-        
-        // set up to run the next time the run loop fires
-        CFRunLoopTimerContext ctx;
-        memset(&ctx, 0, sizeof(ctx));
-        ctx.info = nb;
-        
-        // make a run loop timer
-        CFRunLoopTimerRef timerRef =
-            CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 0,
-                                 0, 0, NotificationPort::HandleRunLoopTimer, &ctx);
-        
-        // install it to be run.
-        CFRunLoopAddTimer(CFRunLoopGetCurrent(), timerRef, kCFRunLoopDefaultMode);
+        // If these exceptions propagate, we crash our enclosing app. That's bad. Worse than silently swallowing the error.
+        catch(CssmError &cssme) {
+            secerror("caught CssmError during notification: %d %s", (int) cssme.error, cssmErrorString(cssme.error));
+        }
+        catch(UnixError &ue) {
+            secerror("caught UnixError during notification: %d %s", ue.unixError(), ue.what());
+        }
+        catch (MacOSError mose) {
+            secerror("caught MacOSError during notification: %d %s", (int) mose.osStatus(), mose.what());
+        }
+        catch (...) {
+            secerror("caught unknown error during notification");
+        }
+    };
+
+    uint32_t status = notify_register_dispatch(GetNotificationName(), &out_token, notification_queue, receive);
+    if (status) {
+        secerror("notify_register_dispatch failed: %d", status);
+        syslog(LOG_ERR, "notify_register_dispatch failed: %d", status);
+    } else {
+        initializationComplete = true;
     }
+    return initializationComplete;
 }
 
 
-
-class ThreadNotifier
-{
-protected:
-       NotificationPort *mNotificationPort;
-       int mNotifyToken;
-
-public:
-       ThreadNotifier();
-       ~ThreadNotifier();
-};
-
-
-
-ThreadNotifier::ThreadNotifier()
-    : mNotificationPort(NULL)
-{
-       mach_port_t mp;
-       if (notify_register_mach_port (GetNotificationName (), &mp, 0, &mNotifyToken) == NOTIFY_STATUS_OK) {
-               mNotificationPort = new NotificationPort (mp);
-               mNotificationPort->enable ();
-       }
-}
-
-
-
-ThreadNotifier::~ThreadNotifier()
-{
-       if (mNotificationPort) {
-               notify_cancel (mNotifyToken);
-               delete mNotificationPort;
-       }
-}
-
-
-
-ModuleNexus<ThreadNexus<ThreadNotifier> > threadInfo;
-
-
-
-static void InitializeNotifications ()
-{
-       threadInfo()(); // cause the notifier for this thread to initialize
-}
-
-
-
 EventListener::EventListener (NotificationDomain domain, NotificationMask eventMask)
-       : mDomain (domain), mMask (eventMask)
+       : mInitialized(false), mDomain (domain), mMask (eventMask)
 {
        // make sure that notifications are turned on.
-       InitializeNotifications ();
+       mInitialized = InitializeNotifications();
 }
 
-
 //
 // StopNotification() is needed on destruction; everyone else cleans up after themselves.
 //
-EventListener::~EventListener ()
-{
-       StLock<Mutex> lock (gNotificationLock ());
-       
-       // find the listener in the list and remove it
-       EventListenerList::iterator it = std::find (gEventListeners ().begin (),
-                                                                                               gEventListeners ().end (),
-                                                                                               this);
-       if (it != gEventListeners ().end ())
-       {
-               gEventListeners ().erase (it);
-       }
+EventListener::~EventListener () {
+    if (initialized()) {
+        StLock<Mutex> lock (gNotificationLock ());
+
+        // find the listener in the list and remove it
+        EventListenerList::iterator it = std::find (gEventListeners ().begin (),
+                                                    gEventListeners ().end (),
+                                                    this);
+        if (it != gEventListeners ().end ())
+        {
+            gEventListeners ().erase (it);
+        }
+    }
 }
 
-
-
 // get rid of the pure virtual
 void EventListener::consume(NotificationDomain, NotificationEvent, const Security::CssmData&)
 {
@@ -298,12 +218,23 @@ void EventListener::consume(NotificationDomain, NotificationEvent, const Securit
 
 
 
-void EventListener::FinishedInitialization(EventListener *eventListener)
-{
-       StLock<Mutex> lock (gNotificationLock ());
-       gEventListeners().push_back (eventListener);
+void EventListener::FinishedInitialization(EventListener *eventListener) {
+    if (eventListener->initialized()) {
+        StLock<Mutex> lock (gNotificationLock ());
+        gEventListeners().push_back (eventListener);
+    }
 }
 
+dispatch_once_t EventListener::queueOnceToken = 0;
+dispatch_queue_t EventListener::notificationQueue = NULL;
+
+dispatch_queue_t EventListener::getNotificationQueue() {
+    dispatch_once(&queueOnceToken, ^{
+        notificationQueue = dispatch_queue_create("com.apple.security.keychain-notification-queue", DISPATCH_QUEUE_SERIAL);
+    });
+
+    return notificationQueue;
+}
 
 
 } // end namespace SecurityServer