+// Look for a listener for a given user ID
+bool SharedMemoryListener::findUID(uid_t uid) {
+ return Listener::testPredicate([uid](const Listener& listener) -> bool {
+ try {
+ // There may be elements in the map that are not SharedMemoryListeners
+ const SharedMemoryListener& smlListener = dynamic_cast<const SharedMemoryListener&>(listener);
+ if (smlListener.mUID == uid)
+ return true;
+ }
+ catch (...) {
+ return false;
+ }
+ return false;
+ }
+ );
+ return false;
+}
+
+void SharedMemoryListener::createDefaultSharedMemoryListener(uid_t uid, gid_t gid) {
+ uid_t fuid = SharedMemoryCommon::fixUID(uid);
+ if (fuid != 0) { // already created when securityd started up
+ if (!SharedMemoryListener::findUID(fuid)) {
+ secdebug("MDSPRIVACY","creating SharedMemoryListener for uid/gid: %d/%d", fuid, gid);
+ // A side effect of creation of a SharedMemoryListener is addition to the ListenerMap
+#ifndef __clang_analyzer__
+ /* __unused auto sml = */ new SharedMemoryListener(SharedMemoryCommon::kDefaultSecurityMessagesName, kSharedMemoryPoolSize, uid, gid);
+#endif // __clang_analyzer__
+ }
+ }
+}
+
+// Simpler local version of PrimaryKeyImpl::getUInt32
+uint32 SharedMemoryListener::getRecordType(const CssmData& val) const {
+ if (val.Length < sizeof(uint32))
+ return 0; // Not really but good enough for here
+
+ const uint8 *pv = val.Data;
+ // @@@ Assumes data written in big endian.
+ uint32 value = (pv[0] << 24) + (pv[1] << 16) + (pv[2] << 8) + pv[3];
+ return value;
+}
+
+bool SharedMemoryListener::isTrustEvent(Notification *notification) {
+ bool trustEvent = false;
+
+ switch (notification->event) {
+ case kSecDefaultChangedEvent:
+ case kSecKeychainListChangedEvent:
+ case kSecTrustSettingsChangedEvent:
+ trustEvent = true;
+ break;
+ case kSecAddEvent:
+ case kSecDeleteEvent:
+ case kSecUpdateEvent:
+ {
+ NameValueDictionary dictionary (notification->data);
+ const NameValuePair *item = dictionary.FindByName(ITEM_KEY);
+ if (item && (CSSM_DB_RECORDTYPE)getRecordType(item->Value()) == CSSM_DL_DB_RECORD_X509_CERTIFICATE) {
+ trustEvent = true;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (trustEvent) {
+ uint32_t result = notify_post(kSecServerCertificateTrustNotification);
+ if (result != NOTIFY_STATUS_OK) {
+ secdebug("MDSPRIVACY","Certificate trust event notification failed: %d", result);
+ }
+ }
+
+ secdebug("MDSPRIVACY","[%03d] Event is %s trust event", mUID, trustEvent?"a":"not a");
+ return trustEvent;
+}
+
+bool SharedMemoryListener::needsPrivacyFilter(Notification *notification) {
+ if (notification->domain == kNotificationDomainPCSC || notification->domain == kNotificationDomainCDSA)
+ return false;
+
+ // kNotificationDomainDatabase = 1, // something happened to a database (aka keychain)
+ switch (notification->event) {
+ case kSecLockEvent: // kNotificationEventLocked
+ case kSecUnlockEvent: // kNotificationEventUnlocked
+ case kSecPasswordChangedEvent: // kNotificationEventPassphraseChanged
+ case kSecDefaultChangedEvent:
+ case kSecKeychainListChangedEvent:
+ case kSecTrustSettingsChangedEvent:
+ return false;
+ case kSecDataAccessEvent:
+ case kSecAddEvent:
+ case kSecDeleteEvent:
+ case kSecUpdateEvent:
+ break;
+ }
+
+ secdebug("MDSPRIVACY","[%03d] Evaluating event %s", mUID, notification->description().c_str());
+
+ NameValueDictionary dictionary (notification->data);
+ const NameValuePair *item = dictionary.FindByName(ITEM_KEY);
+
+ // If we don't have an item, there is nothing to filter
+ if (!item) {
+ secdebug("MDSPRIVACY","[%03d] Item event did not contain an item", mUID);
+ return false;
+ }
+
+ pid_t thisPid = 0;
+ const NameValuePair *pidRef = dictionary.FindByName(PID_KEY);
+ if (pidRef != 0) {
+ thisPid = n2h(*reinterpret_cast<pid_t*>(pidRef->Value().data()));
+ }
+
+ uid_t out_euid = 0;
+ int rx = SharedMemoryListener::get_process_euid(thisPid, out_euid);
+ if (rx != 0) {
+ secdebug("MDSPRIVACY","[%03d] get_process_euid failed (rx=%d), filtering out item", mUID, rx);
+ return true;
+ }
+
+ if (out_euid == mUID) {
+ return false; // Listener owns this item, so no filtering
+ }
+
+ // Allow processes running as root to pass through certificates
+ if (out_euid == 0) {
+ CSSM_DB_RECORDTYPE recordType = getRecordType(item->Value());
+ if (recordType == CSSM_DL_DB_RECORD_X509_CERTIFICATE) {
+ return false;
+ }
+ }
+
+ secdebug("MDSPRIVACY","[%03d] Filtering event %s", mUID, notification->description().c_str());
+ return true;
+}
+