]> git.saurik.com Git - apple/security.git/blobdiff - securityd/src/notifications.cpp
Security-58286.70.7.tar.gz
[apple/security.git] / securityd / src / notifications.cpp
index 7c048134d0e996cade8eeb948d8b8c051a6ac14e..8ef8872bd28e47541c02ef09e3c96f73e0927ee3 100644 (file)
 // notifications - handling of securityd-gated notification messages
 //
 #include <notify.h>
+#include <sys/sysctl.h>
 
 #include "notifications.h"
 #include "server.h"
 #include "connection.h"
+#include "dictionary.h"
+#include "SharedMemoryClient.h"
+
 #include <securityd_client/ucspNotify.h>
 #include <security_utilities/casts.h>
 
+#include <Security/SecKeychain.h>
+#include <Security/SecItemInternal.h>
 
 Listener::ListenerMap& Listener::listeners = *(new Listener::ListenerMap);
 Mutex Listener::setLock(Mutex::recursive);
@@ -72,12 +78,18 @@ void Listener::notify(NotificationDomain domain,
 }
 
 void Listener::notify(NotificationDomain domain,
-       NotificationEvent event, uint32 sequence, const CssmData &data)
+       NotificationEvent event, uint32 sequence, const CssmData &data, audit_token_t auditToken)
 {
        Connection &current = Server::active().connection();
        RefPointer<Notification> message = new Notification(domain, event, sequence, data);
        if (current.inSequence(message)) {
                StLock<Mutex> _(setLock);
+
+               // This is a total layer violation, but no better place to put it
+               uid_t uid = audit_token_to_euid(auditToken);
+               gid_t gid = audit_token_to_egid(auditToken);
+               SharedMemoryListener::createDefaultSharedMemoryListener(uid, gid);
+
                sendNotification(message);
                while (RefPointer<Notification> next = current.popNotification())
                        sendNotification(next);
@@ -86,10 +98,13 @@ void Listener::notify(NotificationDomain domain,
 
 void Listener::sendNotification(Notification *message)
 {
+    secdebug("MDSPRIVACY","Listener::sendNotification for uid/euid: %d/%d", getuid(), geteuid());
+
     for (ListenerMap::const_iterator it = listeners.begin();
             it != listeners.end(); it++) {
                Listener *listener = it->second;
-               if (listener->domain == kNotificationDomainAll || (message->domain == listener->domain && listener->wants(message->event)))
+               if (listener->domain == kNotificationDomainAll ||
+            (message->domain == listener->domain && listener->wants(message->event)))
                        listener->notifyMe(message);
        }
 }
@@ -138,6 +153,10 @@ Listener::Notification::~Notification()
                this, domain, event, sequence);
 }
 
+std::string Listener::Notification::description() const {
+    return SharedMemoryCommon::notificationDescription(domain, event) +
+        ", Seq: " + std::to_string(sequence) + ", Data: " + std::to_string(this->size());
+}
 
 //
 // Jitter buffering
@@ -168,48 +187,210 @@ RefPointer<Listener::Notification> Listener::JitterBuffer::popNotification()
        }
 }
 
+bool Listener::testPredicate(const std::function<bool(const Listener& listener)> test) {
+    StLock<Mutex> _(setLock);
+    for (ListenerMap::const_iterator it = listeners.begin(); it != listeners.end(); it++) {
+        if (test(*(it->second)))
+            return true;
+    }
+    return false;
+}
+
 /*
  * Shared memory listener
  */
 
 
-SharedMemoryListener::SharedMemoryListener(const char* segmentName, SegmentOffsetType segmentSize) :
+SharedMemoryListener::SharedMemoryListener(const char* segmentName, SegmentOffsetType segmentSize, uid_t uid, gid_t gid) :
        Listener (kNotificationDomainAll, kNotificationAllEvents),
-       SharedMemoryServer (segmentName, segmentSize),
+       SharedMemoryServer (segmentName, segmentSize, uid, gid),
        mActive (false)
 {
-       if (segmentName == NULL)
-       {
-               secerror("Attempted to start securityd with a NULL segmentName");
-        abort();
-       }
 }
 
 SharedMemoryListener::~SharedMemoryListener ()
 {
 }
 
+// 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 kSecDataAccessEvent:
+    case kSecKeychainListChangedEvent:
+    case kSecTrustSettingsChangedEvent:
+        return false;
+    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;
+}
+
 const double kServerWait = 0.005; // time in seconds before clients will be notified that data is available
 
 void SharedMemoryListener::notifyMe(Notification* notification)
 {
-       const void* data = notification->data.data();
-       size_t length = notification->data.length();
+    const void* data = notification->data.data();
+    size_t length = notification->data.length();
     /* enforce a maximum size of 16k for notifications */
     if (length > 16384) return;
 
+    isTrustEvent(notification);
+    if (needsPrivacyFilter(notification)) {
+        return; // just drop it
+    }
+
+    secdebug("MDSPRIVACY","[%03d] WriteMessage event %s", mUID, notification->description().c_str());
+
     WriteMessage (notification->domain, notification->event, data, int_cast<size_t, UInt32>(length));
 
-       if (!mActive)
-       {
-               Server::active().setTimer (this, Time::Interval(kServerWait));
-               mActive = true;
-       }
+    if (!mActive)
+    {
+        Server::active().setTimer (this, Time::Interval(kServerWait));
+        mActive = true;
+    }
 }
 
 void SharedMemoryListener::action ()
 {
        secinfo("notify", "Posted notification to clients.");
+    secdebug("MDSPRIVACY","[%03d] Posted notification to clients", mUID);
        notify_post (mSegmentName.c_str ());
        mActive = false;
 }
+
+int SharedMemoryListener::get_process_euid(pid_t pid, uid_t& out_euid) {
+    struct kinfo_proc proc_info = {};
+    int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+    size_t len = sizeof(struct kinfo_proc);
+    int ret = sysctl(mib, (sizeof(mib)/sizeof(int)), &proc_info, &len, NULL, 0);
+
+    out_euid = -1;
+    if (ret == 0) {
+        out_euid = proc_info.kp_eproc.e_ucred.cr_uid;
+    }
+    return ret;
+}