2 * Copyright (c) 2000-2002 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
22 Contains: Code that communicates with processes that install a callback
23 with the Keychain Manager to receive keychain events.
27 #include "CCallbackMgr.h"
32 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacTypes.h>
34 #include <Security/DLDBListCFPref.h>
35 #include <Security/SecCFTypes.h>
36 //#include <Security/Keychain.h>
38 using namespace KeychainCore;
39 using namespace CssmClient;
41 #pragma mark ÑÑÑÑ CallbackInfo ÑÑÑÑ
43 CallbackInfo::CallbackInfo() : mCallback(NULL),mEventMask(0),mContext(NULL)
47 CallbackInfo::CallbackInfo(SecKeychainCallback inCallbackFunction,
48 SecKeychainEventMask inEventMask, void *inContext)
49 : mCallback(inCallbackFunction), mEventMask(inEventMask), mContext(inContext)
53 CallbackInfo::~CallbackInfo()
57 bool CallbackInfo::operator==(const CallbackInfo& other) const
59 return mCallback==other.mCallback;
62 bool CallbackInfo::operator!=(const CallbackInfo& other) const
64 return !(*this==other);
68 #pragma mark ÑÑÑÑ CCallbackMgr ÑÑÑÑ
70 CCallbackMgr *CCallbackMgr::mCCallbackMgr;
72 CCallbackMgr::CCallbackMgr() :
73 // register for receiving Keychain events via CF
74 Observer(Listener::databaseNotifications, Listener::allEvents)
78 CCallbackMgr::~CCallbackMgr()
82 CCallbackMgr& CCallbackMgr::Instance()
85 mCCallbackMgr = new CCallbackMgr();
87 return *mCCallbackMgr;
90 void CCallbackMgr::AddCallback( SecKeychainCallback inCallbackFunction,
91 SecKeychainEventMask inEventMask,
95 CallbackInfo info( inCallbackFunction, inEventMask, inContext );
96 CallbackInfo existingInfo;
99 CallbackInfoListIterator ix = find( CCallbackMgr::Instance().mEventCallbacks.begin(),
100 CCallbackMgr::Instance().mEventCallbacks.end(), info );
102 // make sure it is not already there
103 if ( ix!=CCallbackMgr::Instance().mEventCallbacks.end() )
105 // It's already there. This could mean that the old process died unexpectedly,
106 // so we need to validate the process ID of the existing callback.
107 // On Mac OS X this list is per process so this is always a duplicate
108 MacOSError::throwMe(errSecDuplicateCallback);
111 CCallbackMgr::Instance().mEventCallbacks.push_back(info);
117 SecKeychainCallback mCallbackFunction;
119 Predicate(SecKeychainCallback inCallbackFunction) : mCallbackFunction(inCallbackFunction) {}
120 bool operator()(const CallbackInfo &cbInfo) { return cbInfo.mCallback == mCallbackFunction; }
123 void CCallbackMgr::RemoveCallback(SecKeychainCallback inCallbackFunction)
125 size_t oldSize = CCallbackMgr::Instance().mEventCallbacks.size();
126 Predicate predicate(inCallbackFunction);
127 CCallbackMgr::Instance().mEventCallbacks.remove_if(predicate);
129 if (oldSize == CCallbackMgr::Instance().mEventCallbacks.size())
130 MacOSError::throwMe(errSecInvalidCallback);
133 void CCallbackMgr::AlertClients(const list<CallbackInfo> &eventCallbacks,
134 SecKeychainEvent inEvent,
136 const Keychain &inKeychain,
139 secdebug("kcnotify", "dispatch event %ld pid %d keychain %p item %p",
140 inEvent, inPid, &inKeychain, !!inItem ? &*inItem : NULL);
142 // Iterate through callbacks, looking for those registered for inEvent
143 const SecKeychainEventMask theMask = 1U << inEvent;
145 for (ConstCallbackInfoListIterator ix = eventCallbacks.begin(); ix != eventCallbacks.end(); ++ix)
147 if (!(ix->mEventMask & theMask))
150 SecKeychainCallbackInfo cbInfo;
151 cbInfo.version = 0; // @@@ kKeychainAPIVersion;
152 cbInfo.item = inItem ? inItem->handle() : 0;
153 cbInfo.keychain = inKeychain ? inKeychain->handle() : 0;
156 ix->mCallback(inEvent, &cbInfo, ix->mContext);
157 if (cbInfo.item) CFRelease(cbInfo.item);
158 if (cbInfo.keychain) CFRelease(cbInfo.keychain);
162 /***********************************************************************************
163 * Event() - Overriden function of the KCEventObserver object.
164 * Each instance of KeychainCore will receive events from CF
165 * that was initiated by another KeychainCore instance that
166 * triggered the event.
168 * We <could> care about which KeychainCore posted the event:
169 * Example (KCDeleteItem event):
170 * If it was 'us', we don't do anything; we already processed the event.
171 * If it wasn't 'us', we should remove our cached reference to the item that was deleted.
173 ***********************************************************************************/
174 void CCallbackMgr::Event(Listener::Domain domain, Listener::Event whichEvent, NameValueDictionary &dictionary)
176 // Decode from userInfo the event type, 'keychain' CFDict, and 'item' CFDict
177 SecKeychainEvent thisEvent = whichEvent;
180 const NameValuePair* pidRef = dictionary.FindByName(PID_KEY);
187 thisPid = *reinterpret_cast<pid_t*>(pidRef->Value().data());
190 Keychain thisKeychain = 0;
192 list<CallbackInfo> eventCallbacks;
195 // Lock the global API lock before doing stuff with StorageManager.
196 StLock<Mutex> _(globals().apiLock);
198 // make sure we have a database identifier
199 if (dictionary.FindByName (SSUID_KEY) != 0)
201 DLDbIdentifier dbid = NameValueDictionary::MakeDLDbIdentifierFromNameValueDictionary (dictionary);
202 thisKeychain = globals().storageManager.keychain(dbid);
205 const NameValuePair* item = dictionary.FindByName(ITEM_KEY);
207 if (item && thisKeychain)
209 PrimaryKey pk(item->Value());
210 thisItem = thisKeychain->item(pk);
213 // Deal with events that we care about ourselves first.
214 if (thisEvent == kSecDeleteEvent && thisKeychain.get() && thisItem.get())
215 thisKeychain->didDeleteItem(thisItem.get());
217 eventCallbacks = CCallbackMgr::Instance().mEventCallbacks;
218 // We can safely release the global API lock now since thisKeychain and thisItem
219 // are CFRetained and will be until they go out of scope.
222 // Notify our process of this event.
223 CCallbackMgr::AlertClients(eventCallbacks, thisEvent, thisPid, thisKeychain, thisItem);