/ OSX / libsecurity_keychain / lib / DynamicDLDBList.cpp
DynamicDLDBList.cpp
  1  /*
  2   * Copyright (c) 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      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  
 39  using namespace KeychainCore;
 40  
 41  //
 42  // DynamicDLDBList
 43  //
 44  DynamicDLDBList::DynamicDLDBList()
 45  	: mMutex(Mutex::recursive), mSearchListSet(false)
 46  {
 47  }
 48  
 49  DynamicDLDBList::~DynamicDLDBList()
 50  {
 51  }
 52  
 53  CSSM_RETURN
 54  DynamicDLDBList::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. */
 75  bool
 76  DynamicDLDBList::_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.  */
 82  bool
 83  DynamicDLDBList::_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. */
 97  bool
 98  DynamicDLDBList::_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.  */
105  bool
106  DynamicDLDBList::_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  
124  bool
125  DynamicDLDBList::_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  			secinfo("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  				secinfo("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  
174  const vector<DLDbIdentifier> &
175  DynamicDLDBList::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  
189  void
190  DynamicDLDBList::callback(const Guid &guid, uint32 subserviceID,
191  	CSSM_SERVICE_TYPE subserviceType, CSSM_MODULE_EVENT eventType)
192  {
193  	secinfo("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  			secinfo("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  			secinfo("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  
227  DLDbIdentifier 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  }