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