]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_keychain/lib/CCallbackMgr.cp
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / CCallbackMgr.cp
index 877dc9d08a74573aeb1ff48888c11f9ec46adb48..e85f9c055fcda248e56f954a2672ed17f3a209ed 100644 (file)
@@ -1,15 +1,15 @@
 /*
- * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved.
- * 
+ * Copyright (c) 2000-2004,2011-2016 Apple Inc. All Rights Reserved.
+ *
  * @APPLE_LICENSE_HEADER_START@
- * 
+ *
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
  * compliance with the License. Please obtain a copy of the License at
  * http://www.opensource.apple.com/apsl/ and read it before using this
  * file.
- * 
+ *
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
@@ -17,7 +17,7 @@
  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  * Please see the License for the specific language governing rights and
  * limitations under the License.
- * 
+ *
  * @APPLE_LICENSE_HEADER_END@
  */
 
 #include <list>
 
 #include "Globals.h"
+#include <security_cdsa_utilities/Schema.h>
 #include <security_keychain/SecCFTypes.h>
 #include <securityd_client/SharedMemoryCommon.h>
 #include <securityd_client/ssnotify.h>
+#include <utilities/SecCFRelease.h>
 #include <notify.h>
+#include <Security/SecCertificatePriv.h>
 
 using namespace KeychainCore;
 using namespace CssmClient;
@@ -47,18 +50,31 @@ using namespace SecurityServer;
 
 #pragma mark ÑÑÑÑ CallbackInfo ÑÑÑÑ
 
-CallbackInfo::CallbackInfo() : mCallback(NULL),mEventMask(0),mContext(NULL)
+CallbackInfo::CallbackInfo() : mCallback(NULL),mEventMask(0),mContext(NULL), mRunLoop(NULL), mActive(false)
 {
 }
 
 CallbackInfo::CallbackInfo(SecKeychainCallback inCallbackFunction,
-       SecKeychainEventMask inEventMask, void *inContext)
-       : mCallback(inCallbackFunction), mEventMask(inEventMask), mContext(inContext)
+       SecKeychainEventMask inEventMask, void *inContext, CFRunLoopRef runLoop)
+       : mCallback(inCallbackFunction), mEventMask(inEventMask), mContext(inContext), mRunLoop(NULL), mActive(false)
 {
+    mRunLoop = runLoop;
+    CFRetainSafe(mRunLoop);
+}
+
+CallbackInfo::CallbackInfo(const CallbackInfo& cb) {
+    mCallback = cb.mCallback;
+    mEventMask = cb.mEventMask;
+    mContext = cb.mContext;
+    mActive = cb.mActive;
+
+    mRunLoop = cb.mRunLoop;
+    CFRetainSafe(mRunLoop);
 }
 
 CallbackInfo::~CallbackInfo()
 {
+    CFReleaseNull(mRunLoop);
 }
 
 bool CallbackInfo::operator==(const CallbackInfo& other) const
@@ -110,18 +126,16 @@ CCallbackMgr& CCallbackMgr::Instance()
        return gCallbackMaker().instance();
 }
 
-void CCallbackMgr::AddCallback( SecKeychainCallback inCallbackFunction, 
+void CCallbackMgr::AddCallback( SecKeychainCallback inCallbackFunction,
                              SecKeychainEventMask      inEventMask,
                              void*                     inContext)
 
 {
-       CallbackInfo info( inCallbackFunction, inEventMask, inContext );
-       CallbackInfo existingInfo;
-
+       CallbackInfo info( inCallbackFunction, inEventMask, inContext, CFRunLoopGetCurrent() );
 
     CallbackInfoListIterator ix = find( CCallbackMgr::Instance().mEventCallbacks.begin(),
                                         CCallbackMgr::Instance().mEventCallbacks.end(), info );
-       
+
        // make sure it is not already there
        if ( ix!=CCallbackMgr::Instance().mEventCallbacks.end() )
     {
@@ -130,8 +144,38 @@ void CCallbackMgr::AddCallback( SecKeychainCallback inCallbackFunction,
         // On Mac OS X this list is per process so this is always a duplicate
                MacOSError::throwMe(errSecDuplicateCallback);
        }
-       
+
        CCallbackMgr::Instance().mEventCallbacks.push_back(info);
+
+    // We want to deliver these notifications if the CFRunLoop we just wrote down is actually actively serviced.
+    // Otherwise, it'll be a continuous (undetectable) leak.
+    CFRunLoopTimerContext ctx;
+    memset(&ctx, 0, sizeof(ctx));
+    ctx.info = info.mRunLoop;
+
+    CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 0, 0, 0, CCallbackMgr::cfrunLoopActive, &ctx);
+    secdebug("kcnotify", "adding a activate callback on run loop %p", info.mRunLoop);
+    CFRunLoopAddTimer(info.mRunLoop, timerRef, kCFRunLoopDefaultMode);
+}
+
+void CCallbackMgr::cfrunLoopActive(CFRunLoopTimerRef timer, void* info) {
+    CFRunLoopRef runLoop = (CFRunLoopRef) info;
+    secdebug("kcnotify", "activating run loop %p", runLoop);
+
+    // Use the notification queue to serialize setting the mActive bits
+    static dispatch_queue_t notification_queue = EventListener::getNotificationQueue();
+    dispatch_async(notification_queue, ^() {
+        // Iterate through list, and activate every notification on this run loop
+        for(CallbackInfoListIterator ix = CCallbackMgr::Instance().mEventCallbacks.begin(); ix != CCallbackMgr::Instance().mEventCallbacks.end(); ix++) {
+            // pointer comparison, not CFEqual.
+            if(ix->mRunLoop == runLoop) {
+                secdebug("kcnotify", "activating callback on run loop %p", runLoop);
+                ix->mActive = true;
+            }
+        }
+    });
+
+    CFRelease(timer);
 }
 
 
@@ -153,13 +197,54 @@ void CCallbackMgr::RemoveCallback(SecKeychainCallback inCallbackFunction)
                MacOSError::throwMe(errSecInvalidCallback);
 }
 
+struct CallbackMgrInfo {
+    SecKeychainEvent event;
+    SecKeychainCallbackInfo secKeychainCallbackInfo;
+    SecKeychainCallback callback;
+    void *callbackContext;
+};
+
+void CCallbackMgr::tellClient(CFRunLoopTimerRef timer, void* info) {
+    CallbackMgrInfo* cbmInfo = (CallbackMgrInfo*) info;
+    if(!cbmInfo || !(cbmInfo->callback)) {
+        return;
+    }
+
+    cbmInfo->callback(cbmInfo->event, &(cbmInfo->secKeychainCallbackInfo), cbmInfo->callbackContext);
+    if (cbmInfo->secKeychainCallbackInfo.item) CFRelease(cbmInfo->secKeychainCallbackInfo.item);
+    if (cbmInfo->secKeychainCallbackInfo.keychain) CFRelease(cbmInfo->secKeychainCallbackInfo.keychain);
+    free(cbmInfo);
+    CFRelease(timer);
+}
+
+static SecKeychainItemRef createItemReference(const Item &inItem)
+{
+       SecKeychainItemRef itemRef = (inItem) ? inItem->handle() : 0;
+       if(!itemRef) { return NULL; }
+
+#if SECTRUST_OSX
+       SecItemClass itemClass = Schema::itemClassFor(inItem->recordType());
+       if (itemClass == kSecCertificateItemClass) {
+               SecCertificateRef certRef = SecCertificateCreateFromItemImplInstance((SecCertificateRef)itemRef);
+               CFRelease(itemRef); /* certRef maintains its own internal reference to itemRef */
+               itemRef = (SecKeychainItemRef) certRef;
+       }
+#endif
+       return itemRef;
+}
+
+static SecKeychainRef createKeychainReference(const Keychain &inKeychain)
+{
+       return (inKeychain) ? inKeychain->handle() : 0;
+}
+
 void CCallbackMgr::AlertClients(const list<CallbackInfo> &eventCallbacks,
                                                                SecKeychainEvent inEvent,
                                                                pid_t inPid,
                                 const Keychain &inKeychain,
                                 const Item &inItem)
 {
-    secdebug("kcnotify", "dispatch event %ld pid %d keychain %p item %p",
+    secinfo("kcnotify", "dispatch event %ld pid %d keychain %p item %p",
         (unsigned long)inEvent, inPid, &inKeychain, !!inItem ? &*inItem : NULL);
 
        // Iterate through callbacks, looking for those registered for inEvent
@@ -170,15 +255,37 @@ void CCallbackMgr::AlertClients(const list<CallbackInfo> &eventCallbacks,
                if (!(ix->mEventMask & theMask))
                        continue;
 
-               SecKeychainCallbackInfo cbInfo;
-               cbInfo.version = 0; // @@@ kKeychainAPIVersion;
-               cbInfo.item = inItem ? inItem->handle() : 0;
-               cbInfo.keychain = inKeychain ? inKeychain->handle() : 0;
-               cbInfo.pid = inPid;
+        if(!(ix->mActive)) {
+            // We haven't received our callback from this CFRunLoop yet. Assume it's not being pumped, and don't schedule.
+            secdebug("kcnotify", "not sending event to run loop %p", ix->mRunLoop);
+            continue;
+        }
+
+        // The previous notification system required a CFRunLoop to be executing. Schedule the client's notifications back on their CFRunLoop, just in case it's important.
+        CFRunLoopRef runLoop = ix->mRunLoop;
+        secdebug("kcnotify", "sending event to runloop %p", runLoop);
+
+        // Set up our callback structures
+        CallbackMgrInfo* cbmInfo = (CallbackMgrInfo*) calloc(sizeof(CallbackMgrInfo), 1);
+
+        cbmInfo->secKeychainCallbackInfo.version = 0; // @@@ kKeychainAPIVersion;
+        cbmInfo->secKeychainCallbackInfo.item = createItemReference(inItem);
+        cbmInfo->secKeychainCallbackInfo.keychain = createKeychainReference(inKeychain);
+        cbmInfo->secKeychainCallbackInfo.pid = inPid;
+
+        cbmInfo->event = inEvent;
+        cbmInfo->callback = ix->mCallback;
+        cbmInfo->callbackContext = ix->mContext;
+
+        CFRunLoopTimerContext ctx;
+        memset(&ctx, 0, sizeof(ctx));
+        ctx.info = cbmInfo;
+
+        // make a run loop timer
+        CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 0, 0, 0, CCallbackMgr::tellClient, &ctx);
 
-               ix->mCallback(inEvent, &cbInfo, ix->mContext);
-               if (cbInfo.item) CFRelease(cbInfo.item);
-               if (cbInfo.keychain) CFRelease(cbInfo.keychain);
+        // Actually call the callback the next time the run loop fires
+        CFRunLoopAddTimer(runLoop, timerRef, kCFRunLoopDefaultMode);
        }
 }