1/* 2 * Copyright (c) 2004 Apple Computer, 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 DynamicDLDBList.cpp 27*/ 28 29#include "DynamicDLDBList.h" 30 31#include "Globals.h" 32 33#include <security_utilities/debugging.h> 34#include <security_cdsa_utilities/cssmbridge.h> // For Required() 35#include <security_cdsa_client/mdsclient.h> 36#include <security_cdsa_client/mds_standard.h> 37#include "KCEventNotifier.h" 38 39using namespace KeychainCore; 40 41// 42// DynamicDLDBList 43// 44DynamicDLDBList::DynamicDLDBList() 45 : mMutex(Mutex::recursive), mSearchListSet(false) 46{ 47} 48 49DynamicDLDBList::~DynamicDLDBList() 50{ 51} 52 53CSSM_RETURN 54DynamicDLDBList::appNotifyCallback(const CSSM_GUID *guid, void *context, 55 uint32 subserviceId, CSSM_SERVICE_TYPE subserviceType, CSSM_MODULE_EVENT eventType) 56{ 57 CSSM_RETURN status = 0; 58 try 59 { 60 reinterpret_cast<DynamicDLDBList *>(context)->callback(Guid::required(guid), 61 subserviceId, subserviceType, eventType); 62 } 63 catch (const CommonError &error) 64 { 65 status = error.osStatus(); 66 } 67 catch (...) 68 { 69 } 70 71 return status; 72} 73 74/* Assume mLock is locked already. Add all databases for this module. */ 75bool 76DynamicDLDBList::_add(const Guid &guid, uint32 subserviceID, CSSM_SERVICE_TYPE subserviceType) 77{ 78 return _add(dlDbIdentifier(guid, subserviceID, subserviceType)); 79} 80 81/* Assume mLock is locked already. Add a single database to the searchlist. */ 82bool 83DynamicDLDBList::_add(const DLDbIdentifier &dlDbIdentifier) 84{ 85 StLock<Mutex>_(mMutex); 86 87 if (find(mSearchList.begin(), mSearchList.end(), dlDbIdentifier) == mSearchList.end()) 88 { 89 mSearchList.push_back(dlDbIdentifier); 90 return true; 91 } 92 93 return false; 94} 95 96/* Assume mLock is locked already. Remove all databases for this module. */ 97bool 98DynamicDLDBList::_remove(const Guid &guid, uint32 subserviceID, CSSM_SERVICE_TYPE subserviceType) 99{ 100 return _remove(dlDbIdentifier(guid, subserviceID, subserviceType)); 101} 102 103/* Assume mLock is locked already. Remove a single database from the 104 searchlist. */ 105bool 106DynamicDLDBList::_remove(const DLDbIdentifier &dlDbIdentifier) 107{ 108 StLock<Mutex>_(mMutex); 109 110 // search for subserviceUid but ignore the dbName, which is dynamic 111 for (SearchList::iterator it = mSearchList.begin(); it != mSearchList.end(); it++) 112 if (it->ssuid() == dlDbIdentifier.ssuid()) 113 { 114 mSearchList.erase(it); 115 116 // Remove from the storageManager cache if it was there. 117 globals().storageManager.didRemoveKeychain(dlDbIdentifier); 118 return true; 119 } 120 // not found 121 return false; 122} 123 124bool 125DynamicDLDBList::_load() 126{ 127 StLock<Mutex>_(mMutex); 128 129 bool list_changed = false; 130 MDSClient::Directory &mds = MDSClient::mds(); 131 MDSClient::Table<MDSClient::Common> common(mds); 132 MDSClient::Table<MDSClient::DL> dl(mds); 133 MDSClient::Table<MDSClient::CSP> csp(mds); 134 135 for (MDSClient::Table<MDSClient::Common>::iterator commonIt = 136 common.find(MDSClient::Attribute("DynamicFlag") != false); 137 commonIt != common.end(); ++commonIt) 138 { 139 CSSM_SERVICE_MASK serviceMask = (*commonIt)->serviceMask(); 140 if (serviceMask & CSSM_SERVICE_DL) 141 { 142 string moduleID = (*commonIt)->moduleID(); 143 secdebug("dynamic", "Loading dynamic %sDL module: %s", 144 (serviceMask & CSSM_SERVICE_CSP) ? "CSP/" : "", moduleID.c_str()); 145 146 /* Register module for callbacks and load it. */ 147 Guid moduleGuid(moduleID); 148 CssmClient::Module module(moduleGuid); 149 module->appNotifyCallback(appNotifyCallback, this); 150 module->load(); 151 mModules.push_back(module); 152 153 /* Now that we have registered for notifications, Find all already 154 registered dl subsevices for this module. */ 155 for (MDSClient::Table<MDSClient::DL>::iterator dlIt = 156 dl.find(MDSClient::Attribute("ModuleID") == moduleID); 157 dlIt!= dl.end(); ++dlIt) 158 { 159 uint32 subserviceID = (*dlIt)->subserviceID(); 160 bool hasCSP = csp.find(MDSClient::Attribute("ModuleID") == moduleID 161 && MDSClient::Attribute("SSID") == subserviceID) != csp.end(); 162 163 secdebug("dynamic", "Adding databases from %sDL SSID %lu module: %s", 164 hasCSP ? "CSP/" : "", (unsigned long)subserviceID, moduleID.c_str()); 165 list_changed |= _add(moduleGuid, subserviceID, 166 hasCSP ? CSSM_SERVICE_CSP | CSSM_SERVICE_DL : CSSM_SERVICE_DL); 167 } 168 } 169 } 170 171 return list_changed; 172} 173 174const vector<DLDbIdentifier> & 175DynamicDLDBList::searchList() 176{ 177 StLock<Mutex>_(mMutex); 178 if (!mSearchListSet) 179 { 180 // Load all dynamic DL's so we start receiving notifications. 181 _load(); 182 183 mSearchListSet = true; 184 } 185 186 return mSearchList; 187} 188 189void 190DynamicDLDBList::callback(const Guid &guid, uint32 subserviceID, 191 CSSM_SERVICE_TYPE subserviceType, CSSM_MODULE_EVENT eventType) 192{ 193 secdebug("event", "Received callback from guid: %s ssid: %lu type: %lu event: %lu", 194 guid.toString().c_str(), (unsigned long)subserviceID, (unsigned long)subserviceType, (unsigned long)eventType); 195 196 StLock<Mutex>_(mMutex); 197 198 bool list_changed = false; 199 200 if (subserviceType & CSSM_SERVICE_DL) 201 { 202 if (eventType == CSSM_NOTIFY_INSERT) 203 { 204 /* A DL or CSP/DL was inserted. */ 205 secdebug("dynamic", "%sDL module: %s SSID: %lu inserted", 206 (subserviceType & CSSM_SERVICE_CSP) ? "CSP/" : "", guid.toString().c_str(), (unsigned long)subserviceID); 207 list_changed = _add(guid, subserviceID, subserviceType); 208 } 209 else if (eventType == CSSM_NOTIFY_REMOVE) 210 { 211 /* A DL or CSP/DL was removed. */ 212 secdebug("dynamic", "%sDL module: %s SSID: %lu removed", 213 (subserviceType & CSSM_SERVICE_CSP) ? "CSP/" : "", guid.toString().c_str(), (unsigned long)subserviceID); 214 list_changed = _remove(guid, subserviceID, subserviceType); 215 } 216 } 217 218 if (list_changed) 219 { 220 // Make sure we are not holding mLock nor the StorageManager mLock when we post these events. 221 // @@@ Rather than posting we should simulate a receive since each client will receive this 222 // cssm level notification. 223 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); 224 } 225} 226 227DLDbIdentifier DynamicDLDBList::dlDbIdentifier(const Guid &guid, 228 uint32 subserviceID, CSSM_SERVICE_TYPE subserviceType) 229{ 230 CSSM_VERSION theVersion={}; 231 CssmSubserviceUid ssuid(guid, &theVersion, subserviceID, subserviceType); 232 CssmNetAddress *dbLocation=NULL; 233 234 return DLDbIdentifier(ssuid, NULL, dbLocation); 235} 236