]> git.saurik.com Git - apple/security.git/blob - Keychain/CCallbackMgr.cp
Security-30.1.tar.gz
[apple/security.git] / Keychain / CCallbackMgr.cp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
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
8 * using this file.
9 *
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.
16 */
17
18
19 /*
20 File: CCallbackMgr.cp
21
22 Contains: Code that communicates with processes that install a callback
23 with the Keychain Manager to receive keychain events.
24
25 Written by: Sari Harrison, Craig Mortensen
26
27 Copyright: © 1998-2000 by Apple Computer, Inc., all rights reserved.
28
29 Change History (most recent first):
30
31 To Do:
32 */
33
34 #include "CCallbackMgr.h"
35
36 #include <algorithm>
37 #include <list>
38
39 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacTypes.h>
40 #include "Globals.h"
41 #include <Security/DLDBListCFPref.h>
42
43 //using namespace std;
44 using namespace KeychainCore;
45 using namespace CssmClient;
46
47 static const UInt32 kTicksBetweenIdleEvents = 5L;
48
49 #pragma mark ÑÑÑÑ CallbackInfo ÑÑÑÑ
50
51 CallbackInfo::CallbackInfo() : mCallback(NULL),mEventMask(0),mContext(NULL)
52 {
53 }
54
55 CallbackInfo::CallbackInfo(SecKeychainCallbackProcPtr inCallbackFunction,SecKeychainEventMask inEventMask,void *inContext)
56 : mCallback(inCallbackFunction),mEventMask(inEventMask),mContext(inContext)
57 {
58 }
59
60 CallbackInfo::~CallbackInfo()
61 {
62 }
63
64 bool CallbackInfo::operator==(const CallbackInfo& other) const
65 {
66 return mCallback==other.mCallback;
67 }
68
69 bool CallbackInfo::operator!=(const CallbackInfo& other) const
70 {
71 return !(*this==other);
72 }
73
74
75 #pragma mark ÑÑÑÑ CCallbackMgr ÑÑÑÑ
76
77 CCallbackMgr *CCallbackMgr::mCCallbackMgr;
78
79 CCallbackMgr::CCallbackMgr() :
80 // register for receiving Keychain events via CF
81 Observer( kSecEventNotificationName, NULL, CFNotificationSuspensionBehaviorDeliverImmediately )
82 {
83 }
84
85 CCallbackMgr::~CCallbackMgr()
86 {
87 }
88
89 CCallbackMgr& CCallbackMgr::Instance()
90 {
91 if (!mCCallbackMgr)
92 mCCallbackMgr = new CCallbackMgr();
93
94 return *mCCallbackMgr;
95 }
96
97 void CCallbackMgr::AddCallback( SecKeychainCallbackProcPtr inCallbackFunction,
98 SecKeychainEventMask inEventMask,
99 void* inContext)
100
101 {
102 CallbackInfo info( inCallbackFunction, inEventMask, inContext );
103 CallbackInfo existingInfo;
104
105
106 CallbackInfoListIterator ix = find( CCallbackMgr::Instance().mEventCallbacks.begin(),
107 CCallbackMgr::Instance().mEventCallbacks.end(), info );
108
109 // make sure it is not already there
110 if ( ix!=CCallbackMgr::Instance().mEventCallbacks.end() )
111 {
112 // It's already there. This could mean that the old process died unexpectedly,
113 // so we need to validate the process ID of the existing callback.
114 // On Mac OS X this list is per process so this is always a duplicate
115 MacOSError::throwMe(errSecDuplicateCallback);
116 }
117
118 CCallbackMgr::Instance().mEventCallbacks.push_back(info);
119 }
120
121 #if 0
122 void CCallbackMgr::AddCallbackUPP(KCCallbackUPP inCallbackFunction,
123 KCEventMask inEventMask,
124 void* inContext)
125 {
126 CallbackInfo info( reinterpret_cast<SecKeychainCallbackProcPtr>(inCallbackFunction), inEventMask, inContext );
127 CallbackInfo existingInfo;
128
129 #if TARGET_API_MAC_OS8
130 OSErr err = noErr;
131 err = ::GetCurrentProcess( &info.mProcessID );
132 KCThrowIf_( err );
133 #endif
134
135 CallbackInfoListIterator ix = find( CCallbackMgr::Instance().mEventCallbacks.begin(),
136 CCallbackMgr::Instance().mEventCallbacks.end(), info );
137
138 // make sure it is not already there
139 if ( ix!=CCallbackMgr::Instance().mEventCallbacks.end() )
140 {
141 // It's already there. This could mean that the old process died unexpectedly,
142 // so we need to validate the process ID of the existing callback.
143 #if TARGET_API_MAC_OS8
144 if (ValidProcess(ix->mProcessID)) // existing callback is OK, so don't add this one.
145 MacOSError::throwMe(errKCDuplicateCallback);
146
147 // Process is gone, so remove the old entry
148 CCallbackMgr::Instance().mEventCallbacks.erase(ix);
149 #else
150 // On Mac OS X this list is per process so this is always a duplicate
151 MacOSError::throwMe(errKCDuplicateCallback);
152 #endif
153 }
154
155 CCallbackMgr::Instance().mEventCallbacks.push_back(info);
156 }
157 #endif
158
159
160 class Predicate
161 {
162 SecKeychainCallbackProcPtr mCallbackFunction;
163 public:
164 Predicate(SecKeychainCallbackProcPtr inCallbackFunction) : mCallbackFunction(inCallbackFunction) {}
165 bool operator()(const CallbackInfo &cbInfo) { return cbInfo.mCallback == mCallbackFunction; }
166 };
167
168 void CCallbackMgr::RemoveCallback(SecKeychainCallbackProcPtr inCallbackFunction)
169 {
170 size_t oldSize = CCallbackMgr::Instance().mEventCallbacks.size();
171 Predicate predicate(inCallbackFunction);
172 CCallbackMgr::Instance().mEventCallbacks.remove_if(predicate);
173
174 if (oldSize == CCallbackMgr::Instance().mEventCallbacks.size())
175 MacOSError::throwMe(errSecInvalidCallback);
176 }
177
178 #if 0
179 void CCallbackMgr::RemoveCallbackUPP(KCCallbackUPP inCallbackFunction)
180 {
181 size_t oldSize = CCallbackMgr::Instance().mEventCallbacks.size();
182 Predicate predicate(reinterpret_cast<SecKeychainCallbackProcPtr>(inCallbackFunction));
183 CCallbackMgr::Instance().mEventCallbacks.remove_if(predicate);
184
185 if (oldSize == CCallbackMgr::Instance().mEventCallbacks.size())
186 MacOSError::throwMe(errKCInvalidCallback);
187 }
188 #endif
189
190 bool CCallbackMgr::ThisProcessUsesSystemEvtCallback()
191 {
192 const SecKeychainEventMask theMask = 1 << kSecSystemEvent;
193
194
195 for ( CallbackInfoListIterator ix = CCallbackMgr::Instance().mEventCallbacks.begin();
196 ix!=CCallbackMgr::Instance().mEventCallbacks.end(); ++ix )
197 {
198 if ( ix->mEventMask & theMask)
199 return true;
200 }
201 return false;
202 }
203
204 //%%% jch move this function to SecurityHI
205 bool CCallbackMgr::ThisProcessCanDisplayUI()
206 {
207 return true;
208 }
209
210 #if 0
211 void CCallbackMgr::Idle()
212 {
213 static unsigned long lastTickCount = 0;
214 unsigned long tickCount = ::TickCount( );
215
216 if (tickCount > lastTickCount+kTicksBetweenIdleEvents)
217 {
218 lastTickCount = tickCount;
219 }
220 }
221 #endif
222
223 void CCallbackMgr::AlertClients(SecKeychainEvent inEvent, bool inOKToAllocateMemory)
224 {
225 AlertClients(inEvent, Keychain(), Item(), inOKToAllocateMemory);
226 }
227
228 void CCallbackMgr::AlertClients(SecKeychainEvent inEvent,
229 const Keychain &inKeychain,
230 const Item &inItem,
231 bool inOKToAllocateMemory)
232 {
233 // Deal with events that we care about ourselves first.
234 if (inEvent == kSecDefaultChangedEvent)
235 globals().defaultKeychain.reload(true);
236 else if (inEvent == kSecKeychainListChangedEvent)
237 globals().storageManager.reload(true);
238
239 // Iterate through callbacks, looking for those registered for inEvent
240 const SecKeychainEventMask theMask = 1U << inEvent;
241
242 for ( CallbackInfoListIterator ix = CCallbackMgr::Instance().mEventCallbacks.begin();
243 ix != CCallbackMgr::Instance().mEventCallbacks.end(); ++ix )
244 {
245 if (!(ix->mEventMask & theMask))
246 continue;
247
248 SecKeychainCallbackInfo cbInfo;
249 cbInfo.version = 0; // @@@ kKeychainAPIVersion;
250 cbInfo.item = inItem ? ItemRef::handle(inItem) : 0;
251 cbInfo.keychain = inKeychain ? KeychainRef::handle(inKeychain) : 0;
252
253 #if 0
254 //%%%cpm- need to change keychaincore.i so we don't to the reinterpret_cast
255 // we need a carbon-version of the callbackmgr to register for events
256 // and call the "C" real callback mgr (use the ix->mCallback when this is ready)
257
258 // until then, we rely on CarbonCore for the UPP stuff
259 InvokeKCCallbackUPP(inEvent,reinterpret_cast<KCCallbackInfo*>(&cbInfo),ix->mContext,
260 reinterpret_cast<KCCallbackUPP>(ix->mCallback));
261 #else
262 ix->mCallback(inEvent,&cbInfo,ix->mContext);
263 #endif
264 }
265 }
266
267 /***********************************************************************************
268 * Event() - Overriden function of the KCEventObserver object.
269 * Each instance of KeychainCore will receive events from CF
270 * that was initiated by another KeychainCore instance that
271 * triggered the event.
272 *
273 * We <could> care about which KeychainCore posted the event:
274 * Example (KCDeleteItem event):
275 * If it was 'us', we don't do anything; we already processed the event.
276 * If it wasn't 'us', we should remove our cached reference to the item that was deleted.
277 *
278 ***********************************************************************************/
279 void CCallbackMgr::Event(CFNotificationCenterRef center,
280 CFStringRef name,
281 const void *object,
282 CFDictionaryRef userInfo)
283 {
284 // Decode from userInfo the event type, 'keychain' CFDict, and 'item' CFDict
285 CCFValue event(CFDictionaryGetValue( userInfo, kSecEventTypeKey ));
286 SecKeychainEvent thisEvent = 0;
287 if (!event.hasValue())
288 return;
289
290 thisEvent = sint32( event );
291
292 CFDictionaryRef kc = reinterpret_cast<CFDictionaryRef>
293 (CFDictionaryGetValue(userInfo, kSecEventKeychainKey));
294 Keychain thisKeychain;
295 if (kc)
296 {
297 thisKeychain = globals().storageManager.keychain
298 (DLDbListCFPref::cfDictionaryRefToDLDbIdentifier(kc));
299 }
300
301 CFDataRef item = reinterpret_cast<CFDataRef>
302 (CFDictionaryGetValue(userInfo, kSecEventItemKey));
303 Item thisItem;
304 if (item && thisKeychain)
305 {
306 const CssmData pkData(const_cast<UInt8*>(CFDataGetBytePtr(item)), CFDataGetLength(item));
307 PrimaryKey pk(pkData);
308 thisItem = thisKeychain->item(pk);
309 }
310
311 // Notify our process of this event.
312 CCallbackMgr::AlertClients(thisEvent, thisKeychain, thisItem);
313 }