2 * Copyright (c) 2003-2004,2006,2011-2012,2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 #include <security_utilities/globalizer.h>
26 #include <security_utilities/threading.h>
27 #include "eventlistener.h"
28 #include "SharedMemoryClient.h"
31 #include <sys/syslog.h>
32 #include <Security/SecBasePriv.h>
34 using namespace MachPlusPlus
;
38 namespace SecurityServer
{
40 typedef RefPointer
<EventListener
> EventPointer
;
41 typedef std::list
<EventPointer
> EventListenerList
;
43 static const char* GetNotificationName ()
45 return SharedMemoryCommon::kDefaultSecurityMessagesName
;
50 class SharedMemoryClientMaker
53 SharedMemoryClient mClient
;
56 SharedMemoryClientMaker ();
57 SharedMemoryClient
* Client ();
62 SharedMemoryClientMaker::SharedMemoryClientMaker () : mClient (GetNotificationName (), kSharedMemoryPoolSize
, SharedMemoryCommon::fixUID(getuid()))
64 secdebug("MDSPRIVACY","[%03d] SharedMemoryClientMaker uid: %d, euid: %d, name: %s", mClient
.getUID(), getuid(), geteuid(), GetNotificationName ());
69 SharedMemoryClient
* SharedMemoryClientMaker::Client ()
76 ModuleNexus
<EventListenerList
> gEventListeners
;
77 ModuleNexus
<Mutex
> gNotificationLock
;
78 ModuleNexus
<SharedMemoryClientMaker
> gMemoryClient
;
81 // Note that once we start notifications, we want receive them forever. Don't have a cancel option.
83 static bool InitializeNotifications () {
84 bool initializationComplete
= false;
85 static dispatch_queue_t notification_queue
= EventListener::getNotificationQueue();
87 secdebug("MDSPRIVACY","EventListener Init: uid: %d, euid: %d, name: %s", getuid(), geteuid(), GetNotificationName ());
88 // Initialize the memory client
91 if (gMemoryClient().Client()->uninitialized()) {
92 secdebug("MDSPRIVACY","[%03d] FATAL: InitializeNotifications EventListener uninitialized; process will never get keychain notifications", getuid());
93 return initializationComplete
;
97 notify_handler_t receive
= ^(int token
){
99 SegmentOffsetType length
;
100 UnavailableReason ur
;
104 // Trust the memory client to break our loop here
107 u_int8_t
*buffer
= new u_int8_t
[kSharedMemoryPoolSize
];
109 StLock
<Mutex
> lock (gNotificationLock ());
110 result
= gMemoryClient().Client()->ReadMessage(buffer
, length
, ur
);
113 secdebug("MDSPRIVACY","[%03d] notify_handler ReadMessage ur: %d", getuid(), ur
);
119 // Send this event off to the listeners
121 StLock
<Mutex
> lock (gNotificationLock ());
122 EventListenerList
& eventList
= gEventListeners();
123 std::set
<EventPointer
*> processedListeners
;
125 // route the message to its destination
126 u_int32_t
* ptr
= (u_int32_t
*) buffer
;
128 // we have a message, do the semantics...
129 SecurityServer::NotificationDomain domain
= (SecurityServer::NotificationDomain
) OSSwapBigToHostInt32 (*ptr
++);
130 SecurityServer::NotificationEvent event
= (SecurityServer::NotificationEvent
) OSSwapBigToHostInt32 (*ptr
++);
131 CssmData
data ((u_int8_t
*) ptr
, buffer
+ length
- (u_int8_t
*) ptr
);
133 string descrip
= SharedMemoryCommon::notificationDescription(domain
, event
);
134 secdebug("MDSPRIVACY","[%03d] notify_handler: %s", getuid(), descrip
.c_str());
135 EventListenerList::iterator it
= eventList
.begin ();
136 while (it
!= eventList
.end ())
138 EventPointer ep
= *it
++;
140 * We can't hold the global event lock when processing callback handers
141 * so remember what items we have processes and don't process them again.
142 * and when we have done a callback we loop back and try again.
144 if (processedListeners
.find(&ep
) != processedListeners
.end()) {
147 processedListeners
.insert(&ep
);
149 /* is this event for this Event handler */
150 if (ep
->GetDomain() != domain
|| (ep
->GetMask() & (1 << event
)) == 0)
156 ep
->consume (domain
, event
, data
);
157 } catch (CssmError
&e
) {
158 // If we throw, libnotify will abort the process. Log these...
159 secerror("caught CssmError while processing notification: %d %s", e
.error
, cssmErrorString(e
.error
));
163 * If we have to grab the lock again, start over iteration
165 it
= eventList
.begin ();
172 // If these exceptions propagate, we crash our enclosing app. That's bad. Worse than silently swallowing the error.
173 catch(CssmError
&cssme
) {
174 secerror("caught CssmError during notification: %d %s", (int) cssme
.error
, cssmErrorString(cssme
.error
));
176 catch(UnixError
&ue
) {
177 secerror("caught UnixError during notification: %d %s", ue
.unixError(), ue
.what());
179 catch (MacOSError mose
) {
180 secerror("caught MacOSError during notification: %d %s", (int) mose
.osStatus(), mose
.what());
183 secerror("caught unknown error during notification");
187 uint32_t status
= notify_register_dispatch(GetNotificationName(), &out_token
, notification_queue
, receive
);
189 secerror("notify_register_dispatch failed: %d", status
);
190 syslog(LOG_ERR
, "notify_register_dispatch failed: %d", status
);
192 initializationComplete
= true;
194 return initializationComplete
;
198 EventListener::EventListener (NotificationDomain domain
, NotificationMask eventMask
)
199 : mInitialized(false), mDomain (domain
), mMask (eventMask
)
201 // make sure that notifications are turned on.
202 mInitialized
= InitializeNotifications();
206 // StopNotification() is needed on destruction; everyone else cleans up after themselves.
208 EventListener::~EventListener () {
210 StLock
<Mutex
> lock (gNotificationLock ());
212 // find the listener in the list and remove it
213 EventListenerList::iterator it
= std::find (gEventListeners ().begin (),
214 gEventListeners ().end (),
216 if (it
!= gEventListeners ().end ())
218 gEventListeners ().erase (it
);
223 // get rid of the pure virtual
224 void EventListener::consume(NotificationDomain
, NotificationEvent
, const Security::CssmData
&)
230 void EventListener::FinishedInitialization(EventListener
*eventListener
) {
231 if (eventListener
->initialized()) {
232 StLock
<Mutex
> lock (gNotificationLock ());
233 gEventListeners().push_back (eventListener
);
237 dispatch_once_t
EventListener::queueOnceToken
= 0;
238 dispatch_queue_t
EventListener::notificationQueue
= NULL
;
240 dispatch_queue_t
EventListener::getNotificationQueue() {
241 dispatch_once(&queueOnceToken
, ^{
242 notificationQueue
= dispatch_queue_create("com.apple.security.keychain-notification-queue", DISPATCH_QUEUE_SERIAL
);
245 return notificationQueue
;
249 } // end namespace SecurityServer
250 } // end namespace Security