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>
37 using namespace KeychainCore;
38 using namespace CssmClient;
40 #pragma mark ÑÑÑÑ CallbackInfo ÑÑÑÑ
42 CallbackInfo::CallbackInfo() : mCallback(NULL),mEventMask(0),mContext(NULL)
46 CallbackInfo::CallbackInfo(SecKeychainCallback inCallbackFunction,
47 SecKeychainEventMask inEventMask, void *inContext)
48 : mCallback(inCallbackFunction), mEventMask(inEventMask), mContext(inContext)
52 CallbackInfo::~CallbackInfo()
56 bool CallbackInfo::operator==(const CallbackInfo& other) const
58 return mCallback==other.mCallback;
61 bool CallbackInfo::operator!=(const CallbackInfo& other) const
63 return !(*this==other);
67 #pragma mark ÑÑÑÑ CCallbackMgr ÑÑÑÑ
69 CCallbackMgr *CCallbackMgr::mCCallbackMgr;
71 CCallbackMgr::CCallbackMgr() :
72 // register for receiving Keychain events via CF
73 Observer(kSecEventNotificationName, NULL, CFNotificationSuspensionBehaviorDeliverImmediately)
77 CCallbackMgr::~CCallbackMgr()
81 CCallbackMgr& CCallbackMgr::Instance()
84 mCCallbackMgr = new CCallbackMgr();
86 return *mCCallbackMgr;
89 void CCallbackMgr::AddCallback( SecKeychainCallback inCallbackFunction,
90 SecKeychainEventMask inEventMask,
94 CallbackInfo info( inCallbackFunction, inEventMask, inContext );
95 CallbackInfo existingInfo;
98 CallbackInfoListIterator ix = find( CCallbackMgr::Instance().mEventCallbacks.begin(),
99 CCallbackMgr::Instance().mEventCallbacks.end(), info );
101 // make sure it is not already there
102 if ( ix!=CCallbackMgr::Instance().mEventCallbacks.end() )
104 // It's already there. This could mean that the old process died unexpectedly,
105 // so we need to validate the process ID of the existing callback.
106 // On Mac OS X this list is per process so this is always a duplicate
107 MacOSError::throwMe(errSecDuplicateCallback);
110 CCallbackMgr::Instance().mEventCallbacks.push_back(info);
116 SecKeychainCallback mCallbackFunction;
118 Predicate(SecKeychainCallback inCallbackFunction) : mCallbackFunction(inCallbackFunction) {}
119 bool operator()(const CallbackInfo &cbInfo) { return cbInfo.mCallback == mCallbackFunction; }
122 void CCallbackMgr::RemoveCallback(SecKeychainCallback inCallbackFunction)
124 size_t oldSize = CCallbackMgr::Instance().mEventCallbacks.size();
125 Predicate predicate(inCallbackFunction);
126 CCallbackMgr::Instance().mEventCallbacks.remove_if(predicate);
128 if (oldSize == CCallbackMgr::Instance().mEventCallbacks.size())
129 MacOSError::throwMe(errSecInvalidCallback);
132 void CCallbackMgr::AlertClients(SecKeychainEvent inEvent,
134 const Keychain &inKeychain,
137 // Deal with events that we care about ourselves first.
138 if (inEvent == kSecDefaultChangedEvent)
139 globals().defaultKeychain.reload(true);
140 else if (inEvent == kSecKeychainListChangedEvent)
141 globals().storageManager.reload(true);
143 // Iterate through callbacks, looking for those registered for inEvent
144 const SecKeychainEventMask theMask = 1U << inEvent;
146 for ( CallbackInfoListIterator ix = CCallbackMgr::Instance().mEventCallbacks.begin();
147 ix != CCallbackMgr::Instance().mEventCallbacks.end(); ++ix )
149 if (!(ix->mEventMask & theMask))
152 SecKeychainCallbackInfo cbInfo;
153 cbInfo.version = 0; // @@@ kKeychainAPIVersion;
154 cbInfo.item = inItem ? gTypes().item.handle(*inItem) : 0;
155 cbInfo.keychain = inKeychain ? gTypes().keychain.handle(*inKeychain) : 0;
158 ix->mCallback(inEvent, &cbInfo, ix->mContext);
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(CFNotificationCenterRef center,
177 CFDictionaryRef userInfo)
179 // Decode from userInfo the event type, 'keychain' CFDict, and 'item' CFDict
180 CCFValue event(CFDictionaryGetValue( userInfo, kSecEventTypeKey ));
181 SecKeychainEvent thisEvent = 0;
182 if (!event.hasValue())
185 thisEvent = sint32( event );
187 CFNumberRef pid = reinterpret_cast<CFNumberRef>
188 (CFDictionaryGetValue(userInfo, kSecEventPidKey));
190 if (!pid || !CFNumberGetValue(pid, kCFNumberSInt32Type, &thisPid))
195 CFDictionaryRef kc = reinterpret_cast<CFDictionaryRef>
196 (CFDictionaryGetValue(userInfo, kSecEventKeychainKey));
197 Keychain thisKeychain;
200 thisKeychain = globals().storageManager.keychain
201 (DLDbListCFPref::cfDictionaryRefToDLDbIdentifier(kc));
204 CFDataRef item = reinterpret_cast<CFDataRef>
205 (CFDictionaryGetValue(userInfo, kSecEventItemKey));
207 if (item && thisKeychain)
209 const CssmData pkData(const_cast<UInt8*>(CFDataGetBytePtr(item)), CFDataGetLength(item));
210 PrimaryKey pk(pkData);
211 thisItem = thisKeychain->item(pk);
214 // Notify our process of this event.
215 CCallbackMgr::AlertClients(thisEvent, thisPid, thisKeychain, thisItem);