]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_keychain/lib/CCallbackMgr.cp
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / CCallbackMgr.cp
diff --git a/OSX/libsecurity_keychain/lib/CCallbackMgr.cp b/OSX/libsecurity_keychain/lib/CCallbackMgr.cp
new file mode 100644 (file)
index 0000000..66741f9
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2000-2004,2011-2014 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,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * 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@
+ */
+
+
+/*
+       File:           CCallbackMgr.cp
+
+       Contains:       Code that communicates with processes that install a callback
+                with the Keychain Manager to receive keychain events.
+
+*/
+
+#include "CCallbackMgr.h"
+
+#include <algorithm>
+#include <list>
+
+#include "Globals.h"
+#include <security_keychain/SecCFTypes.h>
+#include <securityd_client/SharedMemoryCommon.h>
+#include <securityd_client/ssnotify.h>
+#include <notify.h>
+
+using namespace KeychainCore;
+using namespace CssmClient;
+using namespace SecurityServer;
+
+#pragma mark ÑÑÑÑ CallbackInfo ÑÑÑÑ
+
+CallbackInfo::CallbackInfo() : mCallback(NULL),mEventMask(0),mContext(NULL)
+{
+}
+
+CallbackInfo::CallbackInfo(SecKeychainCallback inCallbackFunction,
+       SecKeychainEventMask inEventMask, void *inContext)
+       : mCallback(inCallbackFunction), mEventMask(inEventMask), mContext(inContext)
+{
+}
+
+CallbackInfo::~CallbackInfo()
+{
+}
+
+bool CallbackInfo::operator==(const CallbackInfo& other) const
+{
+    return mCallback==other.mCallback;
+}
+
+bool CallbackInfo::operator!=(const CallbackInfo& other) const
+{
+       return !(*this==other);
+}
+
+
+#pragma mark ÑÑÑÑ CCallbackMgr ÑÑÑÑ
+
+
+class CallbackMaker
+{
+protected:
+       RefPointer<CCallbackMgr> mCallbackManager;
+
+public:
+       CallbackMaker();
+       CCallbackMgr& instance() {return *mCallbackManager;}
+};
+
+
+CallbackMaker::CallbackMaker()
+{
+       CCallbackMgr* manager = new CCallbackMgr();
+       mCallbackManager = manager;
+}
+
+
+
+ModuleNexus<CallbackMaker> gCallbackMaker;
+
+CCallbackMgr::CCallbackMgr() : EventListener (kNotificationDomainDatabase, kNotificationAllEvents)
+{
+    EventListener::FinishedInitialization(this);
+}
+
+CCallbackMgr::~CCallbackMgr()
+{
+}
+
+CCallbackMgr& CCallbackMgr::Instance()
+{
+       return gCallbackMaker().instance();
+}
+
+void CCallbackMgr::AddCallback( SecKeychainCallback inCallbackFunction, 
+                             SecKeychainEventMask      inEventMask,
+                             void*                     inContext)
+
+{
+       CallbackInfo info( inCallbackFunction, inEventMask, inContext );
+       CallbackInfo existingInfo;
+
+
+    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() )
+    {
+        // It's already there. This could mean that the old process died unexpectedly,
+        // so we need to validate the process ID of the existing callback.
+        // On Mac OS X this list is per process so this is always a duplicate
+               MacOSError::throwMe(errSecDuplicateCallback);
+       }
+       
+       CCallbackMgr::Instance().mEventCallbacks.push_back(info);
+}
+
+
+class Predicate
+{
+       SecKeychainCallback mCallbackFunction;
+public:
+       Predicate(SecKeychainCallback inCallbackFunction) : mCallbackFunction(inCallbackFunction) {}
+       bool operator()(const CallbackInfo &cbInfo) { return cbInfo.mCallback == mCallbackFunction; }
+};
+
+void CCallbackMgr::RemoveCallback(SecKeychainCallback inCallbackFunction)
+{
+       size_t oldSize = CCallbackMgr::Instance().mEventCallbacks.size();
+       Predicate predicate(inCallbackFunction);
+       CCallbackMgr::Instance().mEventCallbacks.remove_if(predicate);
+
+       if (oldSize == CCallbackMgr::Instance().mEventCallbacks.size())
+               MacOSError::throwMe(errSecInvalidCallback);
+}
+
+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",
+        (unsigned long)inEvent, inPid, &inKeychain, !!inItem ? &*inItem : NULL);
+
+       // Iterate through callbacks, looking for those registered for inEvent
+       const SecKeychainEventMask theMask = 1U << inEvent;
+
+       for (ConstCallbackInfoListIterator ix = eventCallbacks.begin(); ix != eventCallbacks.end(); ++ix)
+       {
+               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;
+
+               ix->mCallback(inEvent, &cbInfo, ix->mContext);
+               if (cbInfo.item) CFRelease(cbInfo.item);
+               if (cbInfo.keychain) CFRelease(cbInfo.keychain);
+       }
+}
+
+
+
+void CCallbackMgr::consume (SecurityServer::NotificationDomain domain, SecurityServer::NotificationEvent whichEvent, const CssmData &data)
+{
+    NameValueDictionary dictionary (data);
+
+    // Decode from userInfo the event type, 'keychain' CFDict, and 'item' CFDict
+       SecKeychainEvent thisEvent = whichEvent;
+
+    pid_t thisPid;
+       const NameValuePair* pidRef = dictionary.FindByName(PID_KEY);
+       if (pidRef == 0)
+       {
+               thisPid = 0;
+       }
+       else
+       {
+               thisPid = n2h(*reinterpret_cast<pid_t*>(pidRef->Value().data ()));
+       }
+
+       Keychain thisKeychain;
+    Item thisItem;
+       list<CallbackInfo> eventCallbacks;
+       {
+               // Lock the global API lock before doing stuff with StorageManager.
+               // make sure we have a database identifier
+               if (dictionary.FindByName (SSUID_KEY) != 0)
+               {
+            StLock<Mutex>_(*globals().storageManager.getStorageManagerMutex());
+                       DLDbIdentifier dbid = NameValueDictionary::MakeDLDbIdentifierFromNameValueDictionary(dictionary);
+                       thisKeychain = globals().storageManager.keychain(dbid);
+               }
+
+               const NameValuePair* item = dictionary.FindByName(ITEM_KEY);
+
+               if (item && thisKeychain)
+               {
+            PrimaryKey pk(item->Value());
+            thisItem = thisKeychain->item(pk);
+               }
+
+               // Deal with events that we care about ourselves first.
+               if (thisEvent == kSecDeleteEvent && thisKeychain.get() && thisItem.get())
+                       thisKeychain->didDeleteItem(thisItem.get());
+               else if (thisEvent == kSecKeychainListChangedEvent)
+                       globals().storageManager.forceUserSearchListReread();
+
+               eventCallbacks = CCallbackMgr::Instance().mEventCallbacks;
+               // We can safely release the global API lock now since thisKeychain and thisItem
+               // are CFRetained and will be until they go out of scope.
+       }
+
+    // Notify our process of this event.
+       CCallbackMgr::AlertClients(eventCallbacks, thisEvent, thisPid, thisKeychain, thisItem);
+}