/* * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* DynamicDLDBList.cpp */ #include "DynamicDLDBList.h" #include "Globals.h" #include #include // For Required() #include #include #include "KCEventNotifier.h" using namespace KeychainCore; // // DynamicDLDBList // DynamicDLDBList::DynamicDLDBList() : mMutex(Mutex::recursive), mSearchListSet(false) { } DynamicDLDBList::~DynamicDLDBList() { } CSSM_RETURN DynamicDLDBList::appNotifyCallback(const CSSM_GUID *guid, void *context, uint32 subserviceId, CSSM_SERVICE_TYPE subserviceType, CSSM_MODULE_EVENT eventType) { CSSM_RETURN status = 0; try { reinterpret_cast(context)->callback(Guid::required(guid), subserviceId, subserviceType, eventType); } catch (const CommonError &error) { status = error.osStatus(); } catch (...) { } return status; } /* Assume mLock is locked already. Add all databases for this module. */ bool DynamicDLDBList::_add(const Guid &guid, uint32 subserviceID, CSSM_SERVICE_TYPE subserviceType) { return _add(dlDbIdentifier(guid, subserviceID, subserviceType)); } /* Assume mLock is locked already. Add a single database to the searchlist. */ bool DynamicDLDBList::_add(const DLDbIdentifier &dlDbIdentifier) { StLock_(mMutex); if (find(mSearchList.begin(), mSearchList.end(), dlDbIdentifier) == mSearchList.end()) { mSearchList.push_back(dlDbIdentifier); return true; } return false; } /* Assume mLock is locked already. Remove all databases for this module. */ bool DynamicDLDBList::_remove(const Guid &guid, uint32 subserviceID, CSSM_SERVICE_TYPE subserviceType) { return _remove(dlDbIdentifier(guid, subserviceID, subserviceType)); } /* Assume mLock is locked already. Remove a single database from the searchlist. */ bool DynamicDLDBList::_remove(const DLDbIdentifier &dlDbIdentifier) { StLock_(mMutex); // search for subserviceUid but ignore the dbName, which is dynamic for (SearchList::iterator it = mSearchList.begin(); it != mSearchList.end(); it++) if (it->ssuid() == dlDbIdentifier.ssuid()) { mSearchList.erase(it); // Remove from the storageManager cache if it was there. globals().storageManager.didRemoveKeychain(dlDbIdentifier); return true; } // not found return false; } bool DynamicDLDBList::_load() { StLock_(mMutex); bool list_changed = false; MDSClient::Directory &mds = MDSClient::mds(); MDSClient::Table common(mds); MDSClient::Table dl(mds); MDSClient::Table csp(mds); for (MDSClient::Table::iterator commonIt = common.find(MDSClient::Attribute("DynamicFlag") != false); commonIt != common.end(); ++commonIt) { CSSM_SERVICE_MASK serviceMask = (*commonIt)->serviceMask(); if (serviceMask & CSSM_SERVICE_DL) { string moduleID = (*commonIt)->moduleID(); secdebug("dynamic", "Loading dynamic %sDL module: %s", (serviceMask & CSSM_SERVICE_CSP) ? "CSP/" : "", moduleID.c_str()); /* Register module for callbacks and load it. */ Guid moduleGuid(moduleID); CssmClient::Module module(moduleGuid); module->appNotifyCallback(appNotifyCallback, this); module->load(); mModules.push_back(module); /* Now that we have registered for notifications, Find all already registered dl subsevices for this module. */ for (MDSClient::Table::iterator dlIt = dl.find(MDSClient::Attribute("ModuleID") == moduleID); dlIt!= dl.end(); ++dlIt) { uint32 subserviceID = (*dlIt)->subserviceID(); bool hasCSP = csp.find(MDSClient::Attribute("ModuleID") == moduleID && MDSClient::Attribute("SSID") == subserviceID) != csp.end(); secdebug("dynamic", "Adding databases from %sDL SSID %lu module: %s", hasCSP ? "CSP/" : "", (unsigned long)subserviceID, moduleID.c_str()); list_changed |= _add(moduleGuid, subserviceID, hasCSP ? CSSM_SERVICE_CSP | CSSM_SERVICE_DL : CSSM_SERVICE_DL); } } } return list_changed; } const vector & DynamicDLDBList::searchList() { StLock_(mMutex); if (!mSearchListSet) { // Load all dynamic DL's so we start receiving notifications. _load(); mSearchListSet = true; } return mSearchList; } void DynamicDLDBList::callback(const Guid &guid, uint32 subserviceID, CSSM_SERVICE_TYPE subserviceType, CSSM_MODULE_EVENT eventType) { secdebug("event", "Received callback from guid: %s ssid: %lu type: %lu event: %lu", guid.toString().c_str(), (unsigned long)subserviceID, (unsigned long)subserviceType, (unsigned long)eventType); StLock_(mMutex); bool list_changed = false; if (subserviceType & CSSM_SERVICE_DL) { if (eventType == CSSM_NOTIFY_INSERT) { /* A DL or CSP/DL was inserted. */ secdebug("dynamic", "%sDL module: %s SSID: %lu inserted", (subserviceType & CSSM_SERVICE_CSP) ? "CSP/" : "", guid.toString().c_str(), (unsigned long)subserviceID); list_changed = _add(guid, subserviceID, subserviceType); } else if (eventType == CSSM_NOTIFY_REMOVE) { /* A DL or CSP/DL was removed. */ secdebug("dynamic", "%sDL module: %s SSID: %lu removed", (subserviceType & CSSM_SERVICE_CSP) ? "CSP/" : "", guid.toString().c_str(), (unsigned long)subserviceID); list_changed = _remove(guid, subserviceID, subserviceType); } } if (list_changed) { // Make sure we are not holding mLock nor the StorageManager mLock when we post these events. // @@@ Rather than posting we should simulate a receive since each client will receive this // cssm level notification. KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); } } DLDbIdentifier DynamicDLDBList::dlDbIdentifier(const Guid &guid, uint32 subserviceID, CSSM_SERVICE_TYPE subserviceType) { CSSM_VERSION theVersion={}; CssmSubserviceUid ssuid(guid, &theVersion, subserviceID, subserviceType); CssmNetAddress *dbLocation=NULL; return DLDbIdentifier(ssuid, NULL, dbLocation); }