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 44using namespace KeychainCore; 45using namespace CssmClient; 46using namespace SecurityServer; 47 48#pragma mark ���� CallbackInfo ���� 49 50CallbackInfo::CallbackInfo() : mCallback(NULL),mEventMask(0),mContext(NULL) 51{ 52} 53 54CallbackInfo::CallbackInfo(SecKeychainCallback inCallbackFunction, 55 SecKeychainEventMask inEventMask, void *inContext) 56 : mCallback(inCallbackFunction), mEventMask(inEventMask), mContext(inContext) 57{ 58} 59 60CallbackInfo::~CallbackInfo() 61{ 62} 63 64bool CallbackInfo::operator==(const CallbackInfo& other) const 65{ 66 return mCallback==other.mCallback; 67} 68 69bool CallbackInfo::operator!=(const CallbackInfo& other) const 70{ 71 return !(*this==other); 72} 73 74 75#pragma mark ���� CCallbackMgr ���� 76 77 78class CallbackMaker 79{ 80protected: 81 RefPointer<CCallbackMgr> mCallbackManager; 82 83public: 84 CallbackMaker(); 85 CCallbackMgr& instance() {return *mCallbackManager;} 86}; 87 88 89CallbackMaker::CallbackMaker() 90{ 91 CCallbackMgr* manager = new CCallbackMgr(); 92 mCallbackManager = manager; 93} 94 95 96 97ModuleNexus<CallbackMaker> gCallbackMaker; 98 99CCallbackMgr::CCallbackMgr() : EventListener (kNotificationDomainDatabase, kNotificationAllEvents) 100{ 101 EventListener::FinishedInitialization(this); 102} 103 104CCallbackMgr::~CCallbackMgr() 105{ 106} 107 108CCallbackMgr& CCallbackMgr::Instance() 109{ 110 return gCallbackMaker().instance(); 111} 112 113void 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 138class Predicate 139{ 140 SecKeychainCallback mCallbackFunction; 141public: 142 Predicate(SecKeychainCallback inCallbackFunction) : mCallbackFunction(inCallbackFunction) {} 143 bool operator()(const CallbackInfo &cbInfo) { return cbInfo.mCallback == mCallbackFunction; } 144}; 145 146void 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 156void 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 187void 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 thisItem = thisKeychain->item(pk); 224 } 225 226 // Deal with events that we care about ourselves first. 227 if (thisEvent == kSecDeleteEvent && thisKeychain.get() && thisItem.get()) 228 thisKeychain->didDeleteItem(thisItem.get()); 229 else if (thisEvent == kSecKeychainListChangedEvent) 230 globals().storageManager.forceUserSearchListReread(); 231 232 eventCallbacks = CCallbackMgr::Instance().mEventCallbacks; 233 // We can safely release the global API lock now since thisKeychain and thisItem 234 // are CFRetained and will be until they go out of scope. 235 } 236 237 // Notify our process of this event. 238 CCallbackMgr::AlertClients(eventCallbacks, thisEvent, thisPid, thisKeychain, thisItem); 239} 240