2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
28 Contains: Code that communicates with processes that install a callback
29 with the Keychain Manager to receive keychain events.
33 #include "CCallbackMgr.h"
38 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacTypes.h>
40 #include <security_keychain/SecCFTypes.h>
41 #include <securityd_client/SharedMemoryCommon.h>
42 #include <securityd_client/ssnotify.h>
45 using namespace KeychainCore;
46 using namespace CssmClient;
47 using namespace SecurityServer;
49 #pragma mark ÑÑÑÑ CallbackInfo ÑÑÑÑ
51 CallbackInfo::CallbackInfo() : mCallback(NULL),mEventMask(0),mContext(NULL)
55 CallbackInfo::CallbackInfo(SecKeychainCallback inCallbackFunction,
56 SecKeychainEventMask inEventMask, void *inContext)
57 : mCallback(inCallbackFunction), mEventMask(inEventMask), mContext(inContext)
61 CallbackInfo::~CallbackInfo()
65 bool CallbackInfo::operator==(const CallbackInfo& other) const
67 return mCallback==other.mCallback;
70 bool CallbackInfo::operator!=(const CallbackInfo& other) const
72 return !(*this==other);
76 #pragma mark ÑÑÑÑ CCallbackMgr ÑÑÑÑ
82 RefPointer<CCallbackMgr> mCallbackManager;
86 CCallbackMgr& instance() {return *mCallbackManager;}
90 CallbackMaker::CallbackMaker()
92 CCallbackMgr* manager = new CCallbackMgr();
93 mCallbackManager = manager;
98 ModuleNexus<CallbackMaker> gCallbackMaker;
100 CCallbackMgr::CCallbackMgr() : EventListener (kNotificationDomainDatabase, kNotificationAllEvents)
102 EventListener::FinishedInitialization(this);
105 CCallbackMgr::~CCallbackMgr()
109 CCallbackMgr& CCallbackMgr::Instance()
111 return gCallbackMaker().instance();
114 void CCallbackMgr::AddCallback( SecKeychainCallback inCallbackFunction,
115 SecKeychainEventMask inEventMask,
119 CallbackInfo info( inCallbackFunction, inEventMask, inContext );
120 CallbackInfo existingInfo;
123 CallbackInfoListIterator ix = find( CCallbackMgr::Instance().mEventCallbacks.begin(),
124 CCallbackMgr::Instance().mEventCallbacks.end(), info );
126 // make sure it is not already there
127 if ( ix!=CCallbackMgr::Instance().mEventCallbacks.end() )
129 // It's already there. This could mean that the old process died unexpectedly,
130 // so we need to validate the process ID of the existing callback.
131 // On Mac OS X this list is per process so this is always a duplicate
132 MacOSError::throwMe(errSecDuplicateCallback);
135 CCallbackMgr::Instance().mEventCallbacks.push_back(info);
141 SecKeychainCallback mCallbackFunction;
143 Predicate(SecKeychainCallback inCallbackFunction) : mCallbackFunction(inCallbackFunction) {}
144 bool operator()(const CallbackInfo &cbInfo) { return cbInfo.mCallback == mCallbackFunction; }
147 void CCallbackMgr::RemoveCallback(SecKeychainCallback inCallbackFunction)
149 size_t oldSize = CCallbackMgr::Instance().mEventCallbacks.size();
150 Predicate predicate(inCallbackFunction);
151 CCallbackMgr::Instance().mEventCallbacks.remove_if(predicate);
153 if (oldSize == CCallbackMgr::Instance().mEventCallbacks.size())
154 MacOSError::throwMe(errSecInvalidCallback);
157 void CCallbackMgr::AlertClients(const list<CallbackInfo> &eventCallbacks,
158 SecKeychainEvent inEvent,
160 const Keychain &inKeychain,
163 secdebug("kcnotify", "dispatch event %ld pid %d keychain %p item %p",
164 inEvent, inPid, &inKeychain, !!inItem ? &*inItem : NULL);
166 // Iterate through callbacks, looking for those registered for inEvent
167 const SecKeychainEventMask theMask = 1U << inEvent;
169 for (ConstCallbackInfoListIterator ix = eventCallbacks.begin(); ix != eventCallbacks.end(); ++ix)
171 if (!(ix->mEventMask & theMask))
174 SecKeychainCallbackInfo cbInfo;
175 cbInfo.version = 0; // @@@ kKeychainAPIVersion;
176 cbInfo.item = inItem ? inItem->handle() : 0;
177 cbInfo.keychain = inKeychain ? inKeychain->handle() : 0;
180 ix->mCallback(inEvent, &cbInfo, ix->mContext);
181 if (cbInfo.item) CFRelease(cbInfo.item);
182 if (cbInfo.keychain) CFRelease(cbInfo.keychain);
188 void CCallbackMgr::consume (SecurityServer::NotificationDomain domain, SecurityServer::NotificationEvent whichEvent, const CssmData &data)
190 NameValueDictionary dictionary (data);
192 // Decode from userInfo the event type, 'keychain' CFDict, and 'item' CFDict
193 SecKeychainEvent thisEvent = whichEvent;
196 const NameValuePair* pidRef = dictionary.FindByName(PID_KEY);
203 thisPid = n2h(*reinterpret_cast<pid_t*>(pidRef->Value().data ()));
206 Keychain thisKeychain;
208 list<CallbackInfo> eventCallbacks;
210 // Lock the global API lock before doing stuff with StorageManager.
211 // make sure we have a database identifier
212 if (dictionary.FindByName (SSUID_KEY) != 0)
214 StLock<Mutex>_(*globals().storageManager.getStorageManagerMutex());
215 DLDbIdentifier dbid = NameValueDictionary::MakeDLDbIdentifierFromNameValueDictionary(dictionary);
216 thisKeychain = globals().storageManager.keychain(dbid);
219 const NameValuePair* item = dictionary.FindByName(ITEM_KEY);
221 if (item && thisKeychain)
223 PrimaryKey pk(item->Value());
224 thisItem = thisKeychain->item(pk);
227 // Deal with events that we care about ourselves first.
228 if (thisEvent == kSecDeleteEvent && thisKeychain.get() && thisItem.get())
229 thisKeychain->didDeleteItem(thisItem.get());
230 else if (thisEvent == kSecKeychainListChangedEvent)
231 globals().storageManager.forceUserSearchListReread();
233 eventCallbacks = CCallbackMgr::Instance().mEventCallbacks;
234 // We can safely release the global API lock now since thisKeychain and thisItem
235 // are CFRetained and will be until they go out of scope.
238 // Notify our process of this event.
239 CCallbackMgr::AlertClients(eventCallbacks, thisEvent, thisPid, thisKeychain, thisItem);