#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>
#include <Security/SecBasePriv.h>
using namespace MachPlusPlus;
namespace Security {
namespace SecurityServer {
typedef RefPointer<EventListener> EventPointer;
typedef std::list<EventPointer> EventListenerList;
static const char* GetNotificationName ()
{
const char* name = getenv (SECURITYSERVER_BOOTSTRAP_ENV);
if (name == NULL)
{
name = SharedMemoryCommon::kDefaultSecurityMessagesName;
}
return name;
}
class SharedMemoryClientMaker
{
private:
SharedMemoryClient mClient;
public:
SharedMemoryClientMaker ();
SharedMemoryClient* Client ();
};
SharedMemoryClientMaker::SharedMemoryClientMaker () : mClient (GetNotificationName (), kSharedMemoryPoolSize, SharedMemoryCommon::fixUID(getuid()))
{
secdebug("MDSPRIVACY","[%03d] SharedMemoryClientMaker uid: %d, euid: %d, name: %s", mClient.getUID(), getuid(), geteuid(), GetNotificationName ());
}
SharedMemoryClient* SharedMemoryClientMaker::Client ()
{
return &mClient;
}
ModuleNexus<EventListenerList> gEventListeners;
ModuleNexus<Mutex> gNotificationLock;
ModuleNexus<SharedMemoryClientMaker> gMemoryClient;
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 ());
gMemoryClient();
if (gMemoryClient().Client()->uninitialized()) {
secdebug("MDSPRIVACY","[%03d] FATAL: InitializeNotifications EventListener uninitialized; process will never get keychain notifications", getuid());
return initializationComplete;
}
int out_token;
notify_handler_t receive = ^(int token){
try {
SegmentOffsetType length;
UnavailableReason ur;
bool result;
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;
}
}
{
StLock<Mutex> lock (gNotificationLock ());
EventListenerList& eventList = gEventListeners();
std::set<EventPointer *> processedListeners;
u_int32_t* ptr = (u_int32_t*) buffer;
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 ())
{
EventPointer ep = *it++;
if (processedListeners.find(&ep) != processedListeners.end()) {
continue;
}
processedListeners.insert(&ep);
if (ep->GetDomain() != domain || (ep->GetMask() & (1 << event)) == 0)
continue;
lock.unlock();
try {
ep->consume (domain, event, data);
} catch (CssmError &e) {
secerror("caught CssmError while processing notification: %d %s", e.error, cssmErrorString(e.error));
}
lock.lock();
it = eventList.begin ();
}
}
delete [] buffer;
}
}
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;
}
EventListener::EventListener (NotificationDomain domain, NotificationMask eventMask)
: mInitialized(false), mDomain (domain), mMask (eventMask)
{
mInitialized = InitializeNotifications();
}
EventListener::~EventListener () {
if (initialized()) {
StLock<Mutex> lock (gNotificationLock ());
EventListenerList::iterator it = std::find (gEventListeners ().begin (),
gEventListeners ().end (),
this);
if (it != gEventListeners ().end ())
{
gEventListeners ().erase (it);
}
}
}
void EventListener::consume(NotificationDomain, NotificationEvent, const Security::CssmData&)
{
}
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;
}
} }