--- /dev/null
+/*
+ * Copyright (c) 2004,2011,2014 Apple 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 <security_utilities/debugging.h>
+#include <security_cdsa_utilities/cssmbridge.h> // For Required()
+#include <security_cdsa_client/mdsclient.h>
+#include <security_cdsa_client/mds_standard.h>
+#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<DynamicDLDBList *>(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<Mutex>_(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<Mutex>_(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<Mutex>_(mMutex);
+
+ bool list_changed = false;
+ MDSClient::Directory &mds = MDSClient::mds();
+ MDSClient::Table<MDSClient::Common> common(mds);
+ MDSClient::Table<MDSClient::DL> dl(mds);
+ MDSClient::Table<MDSClient::CSP> csp(mds);
+
+ for (MDSClient::Table<MDSClient::Common>::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<MDSClient::DL>::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<DLDbIdentifier> &
+DynamicDLDBList::searchList()
+{
+ StLock<Mutex>_(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<Mutex>_(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);
+}