]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurityd/lib/eventlistener.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurityd / lib / eventlistener.cpp
diff --git a/Security/libsecurityd/lib/eventlistener.cpp b/Security/libsecurityd/lib/eventlistener.cpp
new file mode 100644 (file)
index 0000000..93a3c93
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2003-2004,2006,2011-2012,2014 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,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * 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@
+ */
+
+#include <list>
+#include <security_utilities/globalizer.h>
+#include <security_utilities/threading.h>
+#include "eventlistener.h"
+#include "SharedMemoryClient.h"
+#include <notify.h>
+#include "sscommon.h"
+#include <sys/syslog.h>
+
+using namespace MachPlusPlus;
+
+
+namespace Security {
+namespace SecurityServer {
+
+typedef RefPointer<EventListener> EventPointer;
+typedef std::list<EventPointer> EventListenerList;
+
+static const char* GetNotificationName ()
+{
+       // the name we give the client depends on the value of the environment variable "SECURITYSERVER"
+       const char* name = getenv (SECURITYSERVER_BOOTSTRAP_ENV);
+       if (name == NULL)
+       {
+               name = SECURITY_MESSAGES_NAME;
+       }
+       
+       return name;
+}
+
+
+
+class SharedMemoryClientMaker
+{
+private:
+       SharedMemoryClient mClient;
+
+public:
+       SharedMemoryClientMaker ();
+       SharedMemoryClient* Client ();
+};
+
+
+
+SharedMemoryClientMaker::SharedMemoryClientMaker () : mClient (GetNotificationName (), kSharedMemoryPoolSize)
+{
+}
+
+
+
+SharedMemoryClient* SharedMemoryClientMaker::Client ()
+{
+       return &mClient;
+}
+
+
+
+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 ()
+{
+}
+
+
+
+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;
+            }
+        }
+    }
+}
+
+
+
+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);
+}
+
+
+
+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)
+            {
+                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);
+    }
+}
+
+
+
+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)
+{
+       // make sure that notifications are turned on.
+       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);
+       }
+}
+
+
+
+// get rid of the pure virtual
+void EventListener::consume(NotificationDomain, NotificationEvent, const Security::CssmData&)
+{
+}
+
+
+
+void EventListener::FinishedInitialization(EventListener *eventListener)
+{
+       StLock<Mutex> lock (gNotificationLock ());
+       gEventListeners().push_back (eventListener);
+}
+
+
+
+} // end namespace SecurityServer
+} // end namespace Security