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