/ securityd / src / notifications.cpp
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  // Notification message objects
114  //
115  Listener::Notification::Notification(NotificationDomain inDomain,
116  	NotificationEvent inEvent, uint32 seq, const CssmData &inData)
117  	: domain(inDomain), event(inEvent), sequence(seq), data(Allocator::standard(), inData)
118  {
119  	secinfo("notify", "%p notification created domain 0x%x event %d seq %d",
120  		this, domain, event, sequence);
121  }
122  
123  Listener::Notification::~Notification()
124  {
125  	secinfo("notify", "%p notification done domain 0x%x event %d seq %d",
126  		this, domain, event, sequence);
127  }
128  
129  std::string Listener::Notification::description() const {
130      return SharedMemoryCommon::notificationDescription(domain, event) +
131          ", Seq: " + std::to_string(sequence) + ", Data: " + std::to_string(this->size());
132  }
133  
134  //
135  // Jitter buffering
136  //
137  bool 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 {
143  		secinfo("notify-jit", "%p out of sequence (last %d got %d); buffering",
144  			message, mNotifyLast, message->sequence);
145  		mBuffer[message->sequence] = message;	// save for later
146  		return false;			// hold your fire
147  	}
148  }
149  
150  RefPointer<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
158  		secinfo("notify-jit", "%p retrieved from jitter buffer", result.get());
159  		return result;			// return it
160  	}
161  }
162  
163  bool 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  
172  /*
173   * Shared memory listener
174   */
175  
176  
177  SharedMemoryListener::SharedMemoryListener(const char* segmentName, SegmentOffsetType segmentSize, uid_t uid, gid_t gid) :
178  	Listener (kNotificationDomainAll, kNotificationAllEvents),
179  	SharedMemoryServer (segmentName, segmentSize, uid, gid),
180  	mActive (false), mMutex()
181  {
182  }
183  
184  SharedMemoryListener::~SharedMemoryListener ()
185  {
186  }
187  
188  // Look for a listener for a given user ID
189  bool 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  
206  void 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
220  uint32 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  
230  bool 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  
265  bool 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:
275      case kSecKeychainListChangedEvent:
276      case kSecTrustSettingsChangedEvent:
277          return false;
278      case kSecDataAccessEvent:
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  
325  const double kServerWait = 0.005; // time in seconds before clients will be notified that data is available
326  
327  void SharedMemoryListener::notifyMe(Notification* notification)
328  {
329      const void* data = notification->data.data();
330      size_t length = notification->data.length();
331      /* enforce a maximum size of 16k for notifications */
332      if (length > 16384) return;
333  
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  
341      WriteMessage (notification->domain, notification->event, data, int_cast<size_t, UInt32>(length));
342  
343      StLock<Mutex> lock(mMutex);
344      if (!mActive)
345      {
346          Server::active().setTimer (this, Time::Interval(kServerWait));
347          mActive = true;
348      }
349  }
350  
351  void SharedMemoryListener::action ()
352  {
353      StLock<Mutex> lock(mMutex);
354      notify_post (mSegmentName.c_str ());
355  	secinfo("notify", "Posted notification to clients.");
356      secdebug("MDSPRIVACY","[%03d] Posted notification to clients", mUID);
357  	mActive = false;
358  }
359  
360  int 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  }