]> git.saurik.com Git - apple/security.git/blob - securityd/src/notifications.cpp
Security-57740.51.3.tar.gz
[apple/security.git] / securityd / src / notifications.cpp
1 /*
2 * Copyright (c) 2000-2004,2006,2008 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 //
26 // notifications - handling of securityd-gated notification messages
27 //
28 #include <notify.h>
29 #include <sys/sysctl.h>
30
31 #include "notifications.h"
32 #include "server.h"
33 #include "connection.h"
34 #include "dictionary.h"
35 #include "SharedMemoryClient.h"
36
37 #include <securityd_client/ucspNotify.h>
38 #include <security_utilities/casts.h>
39
40 #include <Security/SecKeychain.h>
41 #include <Security/SecItemInternal.h>
42
43 Listener::ListenerMap& Listener::listeners = *(new Listener::ListenerMap);
44 Mutex Listener::setLock(Mutex::recursive);
45
46
47 //
48 // Listener basics
49 //
50 Listener::Listener(NotificationDomain dom, NotificationMask evs, mach_port_t port)
51 : domain(dom), events(evs)
52 {
53 assert(events); // what's the point?
54
55 // register in listener set
56 StLock<Mutex> _(setLock);
57 listeners.insert(ListenerMap::value_type(port, this));
58
59 secinfo("notify", "%p created for domain 0x%x events 0x%x port %d",
60 this, dom, evs, port);
61 }
62
63 Listener::~Listener()
64 {
65 secinfo("notify", "%p destroyed", this);
66 }
67
68
69 //
70 // Send a notification to all registered listeners
71 //
72 void Listener::notify(NotificationDomain domain,
73 NotificationEvent event, const CssmData &data)
74 {
75 RefPointer<Notification> message = new Notification(domain, event, 0, data);
76 StLock<Mutex> _(setLock);
77 sendNotification(message);
78 }
79
80 void Listener::notify(NotificationDomain domain,
81 NotificationEvent event, uint32 sequence, const CssmData &data, audit_token_t auditToken)
82 {
83 Connection &current = Server::active().connection();
84 RefPointer<Notification> message = new Notification(domain, event, sequence, data);
85 if (current.inSequence(message)) {
86 StLock<Mutex> _(setLock);
87
88 // This is a total layer violation, but no better place to put it
89 uid_t uid = audit_token_to_euid(auditToken);
90 gid_t gid = audit_token_to_egid(auditToken);
91 SharedMemoryListener::createDefaultSharedMemoryListener(uid, gid);
92
93 sendNotification(message);
94 while (RefPointer<Notification> next = current.popNotification())
95 sendNotification(next);
96 }
97 }
98
99 void Listener::sendNotification(Notification *message)
100 {
101 secdebug("MDSPRIVACY","Listener::sendNotification for uid/euid: %d/%d", getuid(), geteuid());
102
103 for (ListenerMap::const_iterator it = listeners.begin();
104 it != listeners.end(); it++) {
105 Listener *listener = it->second;
106 if (listener->domain == kNotificationDomainAll ||
107 (message->domain == listener->domain && listener->wants(message->event)))
108 listener->notifyMe(message);
109 }
110 }
111
112
113 //
114 // Handle a port death or deallocation by removing all Listeners using that port.
115 // Returns true iff we had one.
116 //
117 bool Listener::remove(Port port)
118 {
119 typedef ListenerMap::iterator Iterator;
120 StLock<Mutex> _(setLock);
121 pair<Iterator, Iterator> range = listeners.equal_range(port);
122 if (range.first == range.second)
123 return false; // not one of ours
124
125 assert(range.first != listeners.end());
126 secinfo("notify", "remove port %d", port.port());
127 #if !defined(NDEBUG)
128 for (Iterator it = range.first; it != range.second; it++) {
129 assert(it->first == port);
130 secinfo("notify", "%p listener removed", it->second.get());
131 }
132 #endif //NDEBUG
133 listeners.erase(range.first, range.second);
134 port.destroy();
135 return true; // got it
136 }
137
138
139 //
140 // Notification message objects
141 //
142 Listener::Notification::Notification(NotificationDomain inDomain,
143 NotificationEvent inEvent, uint32 seq, const CssmData &inData)
144 : domain(inDomain), event(inEvent), sequence(seq), data(Allocator::standard(), inData)
145 {
146 secinfo("notify", "%p notification created domain 0x%x event %d seq %d",
147 this, domain, event, sequence);
148 }
149
150 Listener::Notification::~Notification()
151 {
152 secinfo("notify", "%p notification done domain 0x%x event %d seq %d",
153 this, domain, event, sequence);
154 }
155
156 std::string Listener::Notification::description() const {
157 return SharedMemoryCommon::notificationDescription(domain, event) +
158 ", Seq: " + std::to_string(sequence) + ", Data: " + std::to_string(this->size());
159 }
160
161 //
162 // Jitter buffering
163 //
164 bool Listener::JitterBuffer::inSequence(Notification *message)
165 {
166 if (message->sequence == mNotifyLast + 1) { // next in sequence
167 mNotifyLast++; // record next sequence
168 return true; // go ahead
169 } else {
170 secinfo("notify-jit", "%p out of sequence (last %d got %d); buffering",
171 message, mNotifyLast, message->sequence);
172 mBuffer[message->sequence] = message; // save for later
173 return false; // hold your fire
174 }
175 }
176
177 RefPointer<Listener::Notification> Listener::JitterBuffer::popNotification()
178 {
179 JBuffer::iterator it = mBuffer.find(mNotifyLast + 1); // have next message?
180 if (it == mBuffer.end())
181 return NULL; // nothing here
182 else {
183 RefPointer<Notification> result = it->second; // save value
184 mBuffer.erase(it); // remove from buffer
185 secinfo("notify-jit", "%p retrieved from jitter buffer", result.get());
186 return result; // return it
187 }
188 }
189
190 bool Listener::testPredicate(const std::function<bool(const Listener& listener)> test) {
191 StLock<Mutex> _(setLock);
192 for (ListenerMap::const_iterator it = listeners.begin(); it != listeners.end(); it++) {
193 if (test(*(it->second)))
194 return true;
195 }
196 return false;
197 }
198
199 /*
200 * Shared memory listener
201 */
202
203
204 SharedMemoryListener::SharedMemoryListener(const char* segmentName, SegmentOffsetType segmentSize, uid_t uid, gid_t gid) :
205 Listener (kNotificationDomainAll, kNotificationAllEvents),
206 SharedMemoryServer (segmentName, segmentSize, uid, gid),
207 mActive (false)
208 {
209 if (segmentName == NULL)
210 {
211 secerror("Attempted to start securityd with a NULL segmentName");
212 abort();
213 }
214 }
215
216 SharedMemoryListener::~SharedMemoryListener ()
217 {
218 }
219
220 // Look for a listener for a given user ID
221 bool SharedMemoryListener::findUID(uid_t uid) {
222 return Listener::testPredicate([uid](const Listener& listener) -> bool {
223 try {
224 // There may be elements in the map that are not SharedMemoryListeners
225 const SharedMemoryListener& smlListener = dynamic_cast<const SharedMemoryListener&>(listener);
226 if (smlListener.mUID == uid)
227 return true;
228 }
229 catch (...) {
230 return false;
231 }
232 return false;
233 }
234 );
235 return false;
236 }
237
238 void SharedMemoryListener::createDefaultSharedMemoryListener(uid_t uid, gid_t gid) {
239 uid_t fuid = SharedMemoryCommon::fixUID(uid);
240 if (fuid != 0) { // already created when securityd started up
241 if (!SharedMemoryListener::findUID(fuid)) {
242 secdebug("MDSPRIVACY","creating SharedMemoryListener for uid/gid: %d/%d", fuid, gid);
243 // A side effect of creation of a SharedMemoryListener is addition to the ListenerMap
244 #ifndef __clang_analyzer__
245 /* __unused auto sml = */ new SharedMemoryListener(SharedMemoryCommon::kDefaultSecurityMessagesName, kSharedMemoryPoolSize, uid, gid);
246 #endif // __clang_analyzer__
247 }
248 }
249 }
250
251 // Simpler local version of PrimaryKeyImpl::getUInt32
252 uint32 SharedMemoryListener::getRecordType(const CssmData& val) const {
253 if (val.Length < sizeof(uint32))
254 return 0; // Not really but good enough for here
255
256 const uint8 *pv = val.Data;
257 // @@@ Assumes data written in big endian.
258 uint32 value = (pv[0] << 24) + (pv[1] << 16) + (pv[2] << 8) + pv[3];
259 return value;
260 }
261
262 bool SharedMemoryListener::isTrustEvent(Notification *notification) {
263 bool trustEvent = false;
264
265 switch (notification->event) {
266 case kSecDefaultChangedEvent:
267 case kSecKeychainListChangedEvent:
268 case kSecTrustSettingsChangedEvent:
269 trustEvent = true;
270 break;
271 case kSecAddEvent:
272 case kSecDeleteEvent:
273 case kSecUpdateEvent:
274 {
275 NameValueDictionary dictionary (notification->data);
276 const NameValuePair *item = dictionary.FindByName(ITEM_KEY);
277 if (item && (CSSM_DB_RECORDTYPE)getRecordType(item->Value()) == CSSM_DL_DB_RECORD_X509_CERTIFICATE) {
278 trustEvent = true;
279 }
280 }
281 break;
282 default:
283 break;
284 }
285
286 if (trustEvent) {
287 uint32_t result = notify_post(kSecServerCertificateTrustNotification);
288 if (result != NOTIFY_STATUS_OK) {
289 secdebug("MDSPRIVACY","Certificate trust event notification failed: %d", result);
290 }
291 }
292
293 secdebug("MDSPRIVACY","[%03d] Event is %s trust event", mUID, trustEvent?"a":"not a");
294 return trustEvent;
295 }
296
297 bool SharedMemoryListener::needsPrivacyFilter(Notification *notification) {
298 if (notification->domain == kNotificationDomainPCSC || notification->domain == kNotificationDomainCDSA)
299 return false;
300
301 // kNotificationDomainDatabase = 1, // something happened to a database (aka keychain)
302 switch (notification->event) {
303 case kSecLockEvent: // kNotificationEventLocked
304 case kSecUnlockEvent: // kNotificationEventUnlocked
305 case kSecPasswordChangedEvent: // kNotificationEventPassphraseChanged
306 case kSecDefaultChangedEvent:
307 case kSecDataAccessEvent:
308 case kSecKeychainListChangedEvent:
309 case kSecTrustSettingsChangedEvent:
310 return false;
311 case kSecAddEvent:
312 case kSecDeleteEvent:
313 case kSecUpdateEvent:
314 break;
315 }
316
317 secdebug("MDSPRIVACY","[%03d] Evaluating event %s", mUID, notification->description().c_str());
318
319 NameValueDictionary dictionary (notification->data);
320 const NameValuePair *item = dictionary.FindByName(ITEM_KEY);
321
322 // If we don't have an item, there is nothing to filter
323 if (!item) {
324 secdebug("MDSPRIVACY","[%03d] Item event did not contain an item", mUID);
325 return false;
326 }
327
328 pid_t thisPid = 0;
329 const NameValuePair *pidRef = dictionary.FindByName(PID_KEY);
330 if (pidRef != 0) {
331 thisPid = n2h(*reinterpret_cast<pid_t*>(pidRef->Value().data()));
332 }
333
334 uid_t out_euid = 0;
335 int rx = SharedMemoryListener::get_process_euid(thisPid, out_euid);
336 if (rx != 0) {
337 secdebug("MDSPRIVACY","[%03d] get_process_euid failed (rx=%d), filtering out item", mUID, rx);
338 return true;
339 }
340
341 if (out_euid == mUID) {
342 return false; // Listener owns this item, so no filtering
343 }
344
345 // Allow processes running as root to pass through certificates
346 if (out_euid == 0) {
347 CSSM_DB_RECORDTYPE recordType = getRecordType(item->Value());
348 if (recordType == CSSM_DL_DB_RECORD_X509_CERTIFICATE) {
349 return false;
350 }
351 }
352
353 secdebug("MDSPRIVACY","[%03d] Filtering event %s", mUID, notification->description().c_str());
354 return true;
355 }
356
357 const double kServerWait = 0.005; // time in seconds before clients will be notified that data is available
358
359 void SharedMemoryListener::notifyMe(Notification* notification)
360 {
361 const void* data = notification->data.data();
362 size_t length = notification->data.length();
363 /* enforce a maximum size of 16k for notifications */
364 if (length > 16384) return;
365
366 isTrustEvent(notification);
367 if (needsPrivacyFilter(notification)) {
368 return; // just drop it
369 }
370
371 secdebug("MDSPRIVACY","[%03d] WriteMessage event %s", mUID, notification->description().c_str());
372
373 WriteMessage (notification->domain, notification->event, data, int_cast<size_t, UInt32>(length));
374
375 if (!mActive)
376 {
377 Server::active().setTimer (this, Time::Interval(kServerWait));
378 mActive = true;
379 }
380 }
381
382 void SharedMemoryListener::action ()
383 {
384 secinfo("notify", "Posted notification to clients.");
385 secdebug("MDSPRIVACY","[%03d] Posted notification to clients", mUID);
386 notify_post (mSegmentName.c_str ());
387 mActive = false;
388 }
389
390 int SharedMemoryListener::get_process_euid(pid_t pid, uid_t& out_euid) {
391 struct kinfo_proc proc_info = {};
392 int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
393 size_t len = sizeof(struct kinfo_proc);
394 int ret = sysctl(mib, (sizeof(mib)/sizeof(int)), &proc_info, &len, NULL, 0);
395
396 out_euid = -1;
397 if (ret == 0) {
398 out_euid = proc_info.kp_eproc.e_ucred.cr_uid;
399 }
400 return ret;
401 }