]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/CCallbackMgr.cp
Security-57337.40.85.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / CCallbackMgr.cp
1 /*
2 * Copyright (c) 2000-2004,2011-2014 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_keychain/SecCFTypes.h>
40 #include <securityd_client/SharedMemoryCommon.h>
41 #include <securityd_client/ssnotify.h>
42 #include <notify.h>
43
44 using namespace KeychainCore;
45 using namespace CssmClient;
46 using namespace SecurityServer;
47
48 #pragma mark ÑÑÑÑ CallbackInfo ÑÑÑÑ
49
50 CallbackInfo::CallbackInfo() : mCallback(NULL),mEventMask(0),mContext(NULL)
51 {
52 }
53
54 CallbackInfo::CallbackInfo(SecKeychainCallback inCallbackFunction,
55 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
78 class CallbackMaker
79 {
80 protected:
81 RefPointer<CCallbackMgr> mCallbackManager;
82
83 public:
84 CallbackMaker();
85 CCallbackMgr& instance() {return *mCallbackManager;}
86 };
87
88
89 CallbackMaker::CallbackMaker()
90 {
91 CCallbackMgr* manager = new CCallbackMgr();
92 mCallbackManager = manager;
93 }
94
95
96
97 ModuleNexus<CallbackMaker> gCallbackMaker;
98
99 CCallbackMgr::CCallbackMgr() : EventListener (kNotificationDomainDatabase, kNotificationAllEvents)
100 {
101 EventListener::FinishedInitialization(this);
102 }
103
104 CCallbackMgr::~CCallbackMgr()
105 {
106 }
107
108 CCallbackMgr& CCallbackMgr::Instance()
109 {
110 return gCallbackMaker().instance();
111 }
112
113 void CCallbackMgr::AddCallback( SecKeychainCallback inCallbackFunction,
114 SecKeychainEventMask inEventMask,
115 void* inContext)
116
117 {
118 CallbackInfo info( inCallbackFunction, inEventMask, inContext );
119 CallbackInfo existingInfo;
120
121
122 CallbackInfoListIterator ix = find( CCallbackMgr::Instance().mEventCallbacks.begin(),
123 CCallbackMgr::Instance().mEventCallbacks.end(), info );
124
125 // make sure it is not already there
126 if ( ix!=CCallbackMgr::Instance().mEventCallbacks.end() )
127 {
128 // It's already there. This could mean that the old process died unexpectedly,
129 // so we need to validate the process ID of the existing callback.
130 // On Mac OS X this list is per process so this is always a duplicate
131 MacOSError::throwMe(errSecDuplicateCallback);
132 }
133
134 CCallbackMgr::Instance().mEventCallbacks.push_back(info);
135 }
136
137
138 class Predicate
139 {
140 SecKeychainCallback mCallbackFunction;
141 public:
142 Predicate(SecKeychainCallback inCallbackFunction) : mCallbackFunction(inCallbackFunction) {}
143 bool operator()(const CallbackInfo &cbInfo) { return cbInfo.mCallback == mCallbackFunction; }
144 };
145
146 void CCallbackMgr::RemoveCallback(SecKeychainCallback inCallbackFunction)
147 {
148 size_t oldSize = CCallbackMgr::Instance().mEventCallbacks.size();
149 Predicate predicate(inCallbackFunction);
150 CCallbackMgr::Instance().mEventCallbacks.remove_if(predicate);
151
152 if (oldSize == CCallbackMgr::Instance().mEventCallbacks.size())
153 MacOSError::throwMe(errSecInvalidCallback);
154 }
155
156 void CCallbackMgr::AlertClients(const list<CallbackInfo> &eventCallbacks,
157 SecKeychainEvent inEvent,
158 pid_t inPid,
159 const Keychain &inKeychain,
160 const Item &inItem)
161 {
162 secdebug("kcnotify", "dispatch event %ld pid %d keychain %p item %p",
163 (unsigned long)inEvent, inPid, &inKeychain, !!inItem ? &*inItem : NULL);
164
165 // Iterate through callbacks, looking for those registered for inEvent
166 const SecKeychainEventMask theMask = 1U << inEvent;
167
168 for (ConstCallbackInfoListIterator ix = eventCallbacks.begin(); ix != eventCallbacks.end(); ++ix)
169 {
170 if (!(ix->mEventMask & theMask))
171 continue;
172
173 SecKeychainCallbackInfo cbInfo;
174 cbInfo.version = 0; // @@@ kKeychainAPIVersion;
175 cbInfo.item = inItem ? inItem->handle() : 0;
176 cbInfo.keychain = inKeychain ? inKeychain->handle() : 0;
177 cbInfo.pid = inPid;
178
179 ix->mCallback(inEvent, &cbInfo, ix->mContext);
180 if (cbInfo.item) CFRelease(cbInfo.item);
181 if (cbInfo.keychain) CFRelease(cbInfo.keychain);
182 }
183 }
184
185
186
187 void CCallbackMgr::consume (SecurityServer::NotificationDomain domain, SecurityServer::NotificationEvent whichEvent, const CssmData &data)
188 {
189 NameValueDictionary dictionary (data);
190
191 // Decode from userInfo the event type, 'keychain' CFDict, and 'item' CFDict
192 SecKeychainEvent thisEvent = whichEvent;
193
194 pid_t thisPid;
195 const NameValuePair* pidRef = dictionary.FindByName(PID_KEY);
196 if (pidRef == 0)
197 {
198 thisPid = 0;
199 }
200 else
201 {
202 thisPid = n2h(*reinterpret_cast<pid_t*>(pidRef->Value().data ()));
203 }
204
205 Keychain thisKeychain;
206 Item thisItem;
207 list<CallbackInfo> eventCallbacks;
208 {
209 // Lock the global API lock before doing stuff with StorageManager.
210 // make sure we have a database identifier
211 if (dictionary.FindByName (SSUID_KEY) != 0)
212 {
213 StLock<Mutex>_(*globals().storageManager.getStorageManagerMutex());
214 DLDbIdentifier dbid = NameValueDictionary::MakeDLDbIdentifierFromNameValueDictionary(dictionary);
215 thisKeychain = globals().storageManager.keychain(dbid);
216 }
217
218 const NameValuePair* item = dictionary.FindByName(ITEM_KEY);
219
220 if (item && thisKeychain)
221 {
222 PrimaryKey pk(item->Value());
223
224 // if this is a deletion event, do the lookup slightly differently
225 if(thisEvent != kSecDeleteEvent) {
226 thisItem = thisKeychain->item(pk);
227 } else {
228 thisItem = thisKeychain->itemdeleted(pk);
229 }
230 }
231
232 // Deal with events that we care about ourselves first.
233 if (thisEvent == kSecDeleteEvent && thisKeychain.get() && thisItem.get())
234 thisKeychain->didDeleteItem(thisItem.get());
235 else if (thisEvent == kSecKeychainListChangedEvent)
236 globals().storageManager.forceUserSearchListReread();
237
238 eventCallbacks = CCallbackMgr::Instance().mEventCallbacks;
239 // We can safely release the global API lock now since thisKeychain and thisItem
240 // are CFRetained and will be until they go out of scope.
241 }
242
243 // Notify our process of this event.
244 CCallbackMgr::AlertClients(eventCallbacks, thisEvent, thisPid, thisKeychain, thisItem);
245 }