]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/CCallbackMgr.cp
Security-57740.60.18.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / CCallbackMgr.cp
1 /*
2 * Copyright (c) 2000-2004,2011-2016 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 /*
26 File: CCallbackMgr.cp
27
28 Contains: Code that communicates with processes that install a callback
29 with the Keychain Manager to receive keychain events.
30
31 */
32
33 #include "CCallbackMgr.h"
34
35 #include <algorithm>
36 #include <list>
37
38 #include "Globals.h"
39 #include <security_cdsa_utilities/Schema.h>
40 #include <security_keychain/SecCFTypes.h>
41 #include <securityd_client/SharedMemoryCommon.h>
42 #include <securityd_client/ssnotify.h>
43 #include <utilities/SecCFRelease.h>
44 #include <notify.h>
45 #include <Security/SecCertificatePriv.h>
46
47 using namespace KeychainCore;
48 using namespace CssmClient;
49 using namespace SecurityServer;
50
51 #pragma mark ÑÑÑÑ CallbackInfo ÑÑÑÑ
52
53 CallbackInfo::CallbackInfo() : mCallback(NULL),mEventMask(0),mContext(NULL), mRunLoop(NULL), mActive(false)
54 {
55 }
56
57 CallbackInfo::CallbackInfo(SecKeychainCallback inCallbackFunction,
58 SecKeychainEventMask inEventMask, void *inContext, CFRunLoopRef runLoop)
59 : mCallback(inCallbackFunction), mEventMask(inEventMask), mContext(inContext), mRunLoop(NULL), mActive(false)
60 {
61 mRunLoop = runLoop;
62 CFRetainSafe(mRunLoop);
63 }
64
65 CallbackInfo::CallbackInfo(const CallbackInfo& cb) {
66 mCallback = cb.mCallback;
67 mEventMask = cb.mEventMask;
68 mContext = cb.mContext;
69 mActive = cb.mActive;
70
71 mRunLoop = cb.mRunLoop;
72 CFRetainSafe(mRunLoop);
73 }
74
75 CallbackInfo::~CallbackInfo()
76 {
77 CFReleaseNull(mRunLoop);
78 }
79
80 bool CallbackInfo::operator==(const CallbackInfo& other) const
81 {
82 return mCallback==other.mCallback;
83 }
84
85 bool CallbackInfo::operator!=(const CallbackInfo& other) const
86 {
87 return !(*this==other);
88 }
89
90
91 #pragma mark ÑÑÑÑ CCallbackMgr ÑÑÑÑ
92
93
94 class CallbackMaker
95 {
96 protected:
97 RefPointer<CCallbackMgr> mCallbackManager;
98
99 public:
100 CallbackMaker();
101 CCallbackMgr& instance() {return *mCallbackManager;}
102 };
103
104
105 CallbackMaker::CallbackMaker()
106 {
107 CCallbackMgr* manager = new CCallbackMgr();
108 mCallbackManager = manager;
109 }
110
111
112
113 ModuleNexus<CallbackMaker> gCallbackMaker;
114
115 CCallbackMgr::CCallbackMgr() : EventListener (kNotificationDomainDatabase, kNotificationAllEvents)
116 {
117 mInitialized = true;
118 EventListener::FinishedInitialization(this);
119 }
120
121 CCallbackMgr::~CCallbackMgr()
122 {
123 }
124
125 CCallbackMgr& CCallbackMgr::Instance()
126 {
127 return gCallbackMaker().instance();
128 }
129
130 void CCallbackMgr::AddCallback( SecKeychainCallback inCallbackFunction,
131 SecKeychainEventMask inEventMask,
132 void* inContext)
133
134 {
135 CallbackInfo info( inCallbackFunction, inEventMask, inContext, CFRunLoopGetCurrent() );
136
137 CallbackInfoListIterator ix = find( CCallbackMgr::Instance().mEventCallbacks.begin(),
138 CCallbackMgr::Instance().mEventCallbacks.end(), info );
139
140 // make sure it is not already there
141 if ( ix!=CCallbackMgr::Instance().mEventCallbacks.end() )
142 {
143 // It's already there. This could mean that the old process died unexpectedly,
144 // so we need to validate the process ID of the existing callback.
145 // On Mac OS X this list is per process so this is always a duplicate
146 MacOSError::throwMe(errSecDuplicateCallback);
147 }
148
149 CCallbackMgr::Instance().mEventCallbacks.push_back(info);
150
151 // We want to deliver these notifications if the CFRunLoop we just wrote down is actually actively serviced.
152 // Otherwise, it'll be a continuous (undetectable) leak.
153 CFRunLoopTimerContext ctx;
154 memset(&ctx, 0, sizeof(ctx));
155 ctx.info = info.mRunLoop;
156
157 CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 0, 0, 0, CCallbackMgr::cfrunLoopActive, &ctx);
158 secdebug("kcnotify", "adding an activate callback on run loop %p", info.mRunLoop);
159 CFRunLoopAddTimer(info.mRunLoop, timerRef, kCFRunLoopDefaultMode);
160 }
161
162 void CCallbackMgr::cfrunLoopActive(CFRunLoopTimerRef timer, void* info) {
163 CFRunLoopRef runLoop = (CFRunLoopRef) info;
164 secdebug("kcnotify", "activating run loop %p", runLoop);
165
166 // Use the notification queue to serialize setting the mActive bits
167 static dispatch_queue_t notification_queue = EventListener::getNotificationQueue();
168 dispatch_async(notification_queue, ^() {
169 // Iterate through list, and activate every notification on this run loop
170 for(CallbackInfoListIterator ix = CCallbackMgr::Instance().mEventCallbacks.begin(); ix != CCallbackMgr::Instance().mEventCallbacks.end(); ix++) {
171 // pointer comparison, not CFEqual.
172 if(ix->mRunLoop == runLoop) {
173 secdebug("kcnotify", "activating callback on run loop %p", runLoop);
174 ix->mActive = true;
175 }
176 }
177 });
178
179 CFRelease(timer);
180 }
181
182 class Predicate
183 {
184 SecKeychainCallback mCallbackFunction;
185 public:
186 Predicate(SecKeychainCallback inCallbackFunction) : mCallbackFunction(inCallbackFunction) {}
187 bool operator()(const CallbackInfo &cbInfo) { return cbInfo.mCallback == mCallbackFunction; }
188 };
189
190 void CCallbackMgr::RemoveCallback(SecKeychainCallback inCallbackFunction)
191 {
192 size_t oldSize = CCallbackMgr::Instance().mEventCallbacks.size();
193 Predicate predicate(inCallbackFunction);
194 CCallbackMgr::Instance().mEventCallbacks.remove_if(predicate);
195
196 if (oldSize == CCallbackMgr::Instance().mEventCallbacks.size())
197 MacOSError::throwMe(errSecInvalidCallback);
198 }
199
200 struct CallbackMgrInfo {
201 SecKeychainEvent event;
202 SecKeychainCallbackInfo secKeychainCallbackInfo;
203 SecKeychainCallback callback;
204 void *callbackContext;
205 };
206
207 void CCallbackMgr::tellClient(CFRunLoopTimerRef timer, void* info) {
208 CallbackMgrInfo* cbmInfo = (CallbackMgrInfo*) info;
209 if(!cbmInfo || !(cbmInfo->callback)) {
210 return;
211 }
212
213 cbmInfo->callback(cbmInfo->event, &(cbmInfo->secKeychainCallbackInfo), cbmInfo->callbackContext);
214 if (cbmInfo->secKeychainCallbackInfo.item) CFRelease(cbmInfo->secKeychainCallbackInfo.item);
215 if (cbmInfo->secKeychainCallbackInfo.keychain) CFRelease(cbmInfo->secKeychainCallbackInfo.keychain);
216 free(cbmInfo);
217 CFRelease(timer);
218 }
219
220 static SecKeychainItemRef createItemReference(const Item &inItem)
221 {
222 SecKeychainItemRef itemRef = (inItem) ? inItem->handle() : 0;
223 if(!itemRef) { return NULL; }
224
225 SecItemClass itemClass = Schema::itemClassFor(inItem->recordType());
226 if (itemClass == kSecCertificateItemClass) {
227 SecCertificateRef certRef = SecCertificateCreateFromItemImplInstance((SecCertificateRef)itemRef);
228 CFRelease(itemRef); /* certRef maintains its own internal reference to itemRef */
229 itemRef = (SecKeychainItemRef) certRef;
230 }
231
232 return itemRef;
233 }
234
235 static SecKeychainRef createKeychainReference(const Keychain &inKeychain)
236 {
237 return (inKeychain) ? inKeychain->handle() : 0;
238 }
239
240 void CCallbackMgr::AlertClients(const list<CallbackInfo> &eventCallbacks,
241 SecKeychainEvent inEvent,
242 pid_t inPid,
243 const Keychain &inKeychain,
244 const Item &inItem)
245 {
246 secinfo("kcnotify", "dispatch event %ld pid %d keychain %p item %p",
247 (unsigned long)inEvent, inPid, &inKeychain, !!inItem ? &*inItem : NULL);
248
249 // Iterate through callbacks, looking for those registered for inEvent
250 const SecKeychainEventMask theMask = 1U << inEvent;
251
252 for (ConstCallbackInfoListIterator ix = eventCallbacks.begin(); ix != eventCallbacks.end(); ++ix)
253 {
254 if (!(ix->mEventMask & theMask))
255 continue;
256
257 if(!(ix->mActive)) {
258 // We haven't received our callback from this CFRunLoop yet. Assume it's not being pumped, and don't schedule.
259 secdebug("kcnotify", "not sending event to run loop %p", ix->mRunLoop);
260 continue;
261 }
262
263 // The previous notification system required a CFRunLoop to be executing. Schedule the client's notifications back on their CFRunLoop, just in case it's important.
264 CFRunLoopRef runLoop = ix->mRunLoop;
265 secdebug("kcnotify", "sending event to runloop %p", runLoop);
266
267 // Set up our callback structures
268 CallbackMgrInfo* cbmInfo = (CallbackMgrInfo*) calloc(sizeof(CallbackMgrInfo), 1);
269
270 cbmInfo->secKeychainCallbackInfo.version = 0; // @@@ kKeychainAPIVersion;
271 cbmInfo->secKeychainCallbackInfo.item = createItemReference(inItem);
272 cbmInfo->secKeychainCallbackInfo.keychain = createKeychainReference(inKeychain);
273 cbmInfo->secKeychainCallbackInfo.pid = inPid;
274
275 cbmInfo->event = inEvent;
276 cbmInfo->callback = ix->mCallback;
277 cbmInfo->callbackContext = ix->mContext;
278
279 CFRunLoopTimerContext ctx;
280 memset(&ctx, 0, sizeof(ctx));
281 ctx.info = cbmInfo;
282
283 // make a run loop timer
284 CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 0, 0, 0, CCallbackMgr::tellClient, &ctx);
285
286 // Actually call the callback the next time the run loop fires
287 CFRunLoopAddTimer(runLoop, timerRef, kCFRunLoopDefaultMode);
288 }
289 }
290
291
292
293 void CCallbackMgr::consume (SecurityServer::NotificationDomain domain, SecurityServer::NotificationEvent whichEvent, const CssmData &data)
294 {
295 NameValueDictionary dictionary (data);
296
297 // Decode from userInfo the event type, 'keychain' CFDict, and 'item' CFDict
298 SecKeychainEvent thisEvent = (SecKeychainEvent) whichEvent;
299
300 pid_t thisPid;
301 const NameValuePair* pidRef = dictionary.FindByName(PID_KEY);
302 if (pidRef == 0)
303 {
304 thisPid = 0;
305 }
306 else
307 {
308 thisPid = n2h(*reinterpret_cast<pid_t*>(pidRef->Value().data ()));
309 }
310
311 Keychain thisKeychain;
312 Item thisItem;
313 list<CallbackInfo> eventCallbacks;
314 {
315 // Lock the global API lock before doing stuff with StorageManager.
316 // make sure we have a database identifier
317 if (dictionary.FindByName (SSUID_KEY) != 0)
318 {
319 StLock<Mutex>_(*globals().storageManager.getStorageManagerMutex());
320 DLDbIdentifier dbid = NameValueDictionary::MakeDLDbIdentifierFromNameValueDictionary(dictionary);
321 thisKeychain = globals().storageManager.keychain(dbid);
322 globals().storageManager.tickleKeychain(thisKeychain);
323 }
324
325 const NameValuePair* item = dictionary.FindByName(ITEM_KEY);
326
327 if (item && thisKeychain)
328 {
329 PrimaryKey pk(item->Value());
330
331 // if this is a deletion event, do the lookup slightly differently
332 if(thisEvent != kSecDeleteEvent) {
333 thisItem = thisKeychain->item(pk);
334 } else {
335 thisItem = thisKeychain->itemdeleted(pk);
336 }
337 }
338
339 // Deal with events that we care about ourselves first.
340 if (thisEvent == kSecDeleteEvent && thisKeychain.get() && thisItem.get())
341 thisKeychain->didDeleteItem(thisItem.get());
342 else if (thisEvent == kSecKeychainListChangedEvent)
343 globals().storageManager.forceUserSearchListReread();
344
345 eventCallbacks = CCallbackMgr::Instance().mEventCallbacks;
346 // We can safely release the global API lock now since thisKeychain and thisItem
347 // are CFRetained and will be until they go out of scope.
348 }
349
350 // Notify our process of this event.
351 CCallbackMgr::AlertClients(eventCallbacks, thisEvent, thisPid, thisKeychain, thisItem);
352 }