/ OSX / libsecurity_cdsa_client / lib / cssmclient.cpp
cssmclient.cpp
  1  /*
  2   * Copyright (c) 2000-2001,2011-2014 Apple Inc. All Rights Reserved.
  3   * 
  4   * The contents of this file constitute Original Code as defined in and are
  5   * subject to the Apple Public Source License Version 1.2 (the 'License').
  6   * You may not use this file except in compliance with the License. Please obtain
  7   * a copy of the License at http://www.apple.com/publicsource and read it before
  8   * using this file.
  9   * 
 10   * This Original Code and all software distributed under the License are
 11   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
 12   * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
 13   * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 14   * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
 15   * specific language governing rights and limitations under the License.
 16   */
 17  
 18  
 19  //
 20  // cssmclient - common client interface to CSSM and MDS.
 21  //
 22  // Locking Strategy (preliminary):
 23  // XXX This is obsolete update this --mb
 24  // A CssmObject is a CountingMutex. Its count represents the number of children that have registered
 25  // themselves (using addChild/removeChild). The lock controls the internal management fields of the
 26  // various subclasses to protect them against corruption. It does NOT control attribute and argument
 27  // fields and operations, not does it control object-constant fields.
 28  // This means that if you use an object from multiple threads, you (the caller) must lock the object
 29  // during set/get calls of attributes. Note that the CSSM operations themselves are safely multithreaded
 30  // and thus don't need to be interlocked explicitly.
 31  //
 32  #include <security_cdsa_client/cssmclient.h>
 33  #include <utilities/debugging.h>
 34  
 35  using namespace CssmClient;
 36  
 37  //
 38  // Exception model
 39  //
 40  const char *
 41  Error::what () const _NOEXCEPT
 42  {
 43  	return "CSSM client library error";
 44  }
 45  
 46  
 47  //
 48  // General utilities
 49  //
 50  void
 51  ObjectImpl::check(CSSM_RETURN status)
 52  {
 53  	if (status != CSSM_OK)
 54  	{
 55  		CssmError::throwMe(status);
 56  	}
 57  }
 58  
 59  
 60  //
 61  // Common features of Objects
 62  //
 63  ObjectImpl::ObjectImpl() : mParent(), mChildCount(0)
 64  {
 65  	mActive = false;		// not activated
 66  	mAllocator = NULL;		// allocator to be determined
 67  }
 68  
 69  ObjectImpl::ObjectImpl(const Object &mommy) : mParent(mommy.mImpl), mChildCount(0)
 70  {
 71  	mActive = false;		// not activated
 72  	mAllocator = NULL;		// allocator to be determined
 73  	if (mParent)
 74  		mParent->addChild();
 75  }
 76  
 77  ObjectImpl::~ObjectImpl()
 78  try
 79  {
 80      if (!isIdle())
 81      {
 82          int i = mChildCount;
 83          secerror("Object %p still has %d children at delete.\n", this, i);
 84      }
 85  		
 86  	// release parent from her obligations (if we still have one)
 87  	if (mParent)
 88  		mParent->removeChild();
 89  }
 90  catch(...)
 91  {
 92      return;	// Prevent re-throw of exception [function-try-block]
 93  }
 94  
 95  void
 96  ObjectImpl::addChild()
 97  {
 98  	mChildCount++;		// atomic
 99  }
100  
101  void
102  ObjectImpl::removeChild()
103  {
104  	mChildCount--;		// atomic
105  }
106  
107  
108  //
109  // Manage allocators in the Object tree
110  //
111  Allocator &
112  ObjectImpl::allocator() const
113  {
114      StLock<Mutex> _(mAllocatorMutex);
115  	if (mAllocator == NULL)
116  	{
117  		// fix allocator now
118  		if (mParent)
119  			mAllocator = &mParent->allocator();
120  		else
121  			mAllocator = &Allocator::standard();
122  	}
123  
124  	return *mAllocator;
125  }
126  
127  void
128  ObjectImpl::allocator(Allocator &alloc)
129  {
130      StLock<Mutex> _(mAllocatorMutex);
131  	assert(mAllocator == NULL);	// cannot redefine allocator once set
132  	mAllocator = &alloc;
133  }
134  
135  // Comparison operators use pointer comparison by default.  Subclasses may override.
136  bool
137  ObjectImpl::operator <(const ObjectImpl &other) const
138  {
139  	return this < &other;
140  }
141  
142  bool
143  ObjectImpl::operator ==(const ObjectImpl &other) const
144  {
145  	return this == &other;
146  }
147  
148  
149  //
150  // CSSMSession objects.
151  // parent ::= NULL (none)
152  // active ::= CSSM initialized
153  //
154  ModuleNexus<CssmImpl::StandardCssm> CssmImpl::mStandard;
155  
156  CssmImpl::CssmImpl() : ObjectImpl()
157  {
158  	setup();
159  	mStandard().setCssm(this);
160  }
161  
162  CssmImpl::CssmImpl(bool) : ObjectImpl()
163  {
164  	setup();
165  	// implicitly constructed - caller responsible for standard session management
166  }
167  
168  CssmImpl::~CssmImpl()
169  {
170      try
171      {
172  		deactivate();
173      }
174  	catch(...) {}
175  
176  	// this may be the standard session...
177      mStandard().unsetCssm(this);
178  }
179  
180  
181  void
182  CssmImpl::setup()
183  {
184  	// set default configuration
185  	mVersion.Major = 2;
186  	mVersion.Minor = 0;
187  	mScope = CSSM_PRIVILEGE_SCOPE_PROCESS;
188  }
189  
190  
191  Cssm
192  CssmImpl::standard()
193  {
194      return Cssm(mStandard().get());
195  }
196  
197  
198  void
199  CssmImpl::activate()
200  {
201      StLock<Mutex> _(mActivateMutex);
202  	if (!mActive)
203  	{
204  		// currently, no choices on PVC mode and key hierarchy
205  		CSSM_PVC_MODE pvc = CSSM_PVC_NONE;
206  		switch (CSSM_RETURN rc = CSSM_Init(&mVersion,
207  				mScope, &mCallerGuid,
208  				CSSM_KEY_HIERARCHY_NONE, &pvc, NULL)) {
209  		case CSSMERR_CSSM_PVC_ALREADY_CONFIGURED:
210  		case CSSM_OK:
211  			break;
212  		default:
213  			check(rc);
214  		}
215  		mActive = true;
216  	}
217  }
218  
219  void
220  CssmImpl::deactivate()
221  {
222      StLock<Mutex> _(mActivateMutex);
223  	if (mActive)
224  	{
225  		mActive = false;
226  
227  		// clear module map (all gone now)
228  		moduleMap.erase(moduleMap.begin(), moduleMap.end());
229  	
230  		// now terminate CSSM
231  		check(CSSM_Terminate());
232  	}
233  }
234  
235  void
236  CssmImpl::atExitHandler()
237  {
238      try {
239          mStandard.reset();
240      } catch (...) {
241      }
242  }
243  
244  void
245  CssmImpl::catchExit()
246  {
247  	// @@@ Even though this is the "right thing" to do.  This only causes
248  	// exceptions during exit and doesn't really help cleanup correctly.
249  #if 0
250  	if (::atexit(atExitHandler))
251  		UnixError::throwMe();
252  #endif
253  }
254  
255  
256  //
257  // Manage the automatic Cssm object.
258  // This is a program global.
259  //
260  void CssmImpl::StandardCssm::setCssm(CssmImpl *cssm)
261  {
262      StLock<Mutex> _(*this);
263      if (mCssm == NULL)
264          mCssm = cssm;
265  }
266  
267  void CssmImpl::StandardCssm::unsetCssm(CssmImpl *cssm)
268  {
269      StLock<Mutex> _(*this);
270      if (mCssm == cssm)
271          mCssm = NULL;
272  }
273  
274  Cssm CssmImpl::StandardCssm::get()
275  {
276      StLock<Mutex> _(*this);
277      if (mCssm == NULL) {	// make the default instance
278          mCssm = new CssmImpl(true);
279      }
280      return Cssm(mCssm);
281  }
282  
283  CssmImpl::StandardCssm::~StandardCssm()
284  {
285      if (mCssm) {
286          mCssm->deactivate();
287          delete mCssm;
288      }
289  }
290  
291  
292  //
293  // Auto-module management
294  //
295  Module
296  CssmImpl::autoModule(const Guid &guid)
297  {
298  	StLock<Mutex> _(mapLock);
299  	ModuleMap::iterator it = moduleMap.find(guid);
300  	if (it == moduleMap.end())
301  	{
302  		// no automodule for this guid yet, create one
303  		Module module(guid, Cssm(this));
304  		moduleMap.insert(ModuleMap::value_type(guid, module));
305  		return module;
306  	}
307  	else
308  	{
309  		// existing automodule - use it
310  		return it->second;
311  	}
312  }
313  
314  
315  //
316  // Module objects.
317  // parent ::= the session object (usually Cssm::standard)
318  // active ::= module is loaded.
319  //
320  ModuleImpl::ModuleImpl(const Guid &guid) : ObjectImpl(Cssm::standard()),
321  	mAppNotifyCallback(NULL),
322  	mAppNotifyCallbackCtx(NULL)
323  {
324  	setGuid(guid);
325  }
326  
327  ModuleImpl::ModuleImpl(const Guid &guid, const Cssm &session) : ObjectImpl(session),
328  	mAppNotifyCallback(NULL),
329  	mAppNotifyCallbackCtx(NULL)
330  {
331  	setGuid(guid);
332  }
333  
334  ModuleImpl::~ModuleImpl()
335  {
336  	unload();
337  }
338  
339  
340  //
341  // RawModuleEvent objects encapsulate CSSM module callbacks
342  //
343  RawModuleEvents::~RawModuleEvents()
344  { }
345  
346  CSSM_RETURN RawModuleEvents::sendNotify(const CSSM_GUID *, void *context,
347  	uint32 subService, CSSM_SERVICE_TYPE type, CSSM_MODULE_EVENT event)
348  {
349  	try {
350  		reinterpret_cast<RawModuleEvents *>(context)->notify(subService, type, event);
351  		return CSSM_OK;
352  	} catch (const CommonError &error) {
353  		return CssmError::cssmError(error, CSSM_CSSM_BASE_ERROR);
354  	} catch (...) {
355  		return CSSMERR_CSSM_INTERNAL_ERROR;	// whatever...
356  	}
357  }
358  
359  
360  //
361  // ModuleEvents enhance RawModuleEvents by splitting the callback up by type
362  //
363  void ModuleEvents::notify(uint32 subService,
364  	CSSM_SERVICE_TYPE type, CSSM_MODULE_EVENT event)
365  {
366  	switch (event) {
367  	case CSSM_NOTIFY_INSERT:
368  		insertion(subService, type);
369  		break;
370  	case CSSM_NOTIFY_REMOVE:
371  		removal(subService, type);
372  		break;
373  	case CSSM_NOTIFY_FAULT:
374  		fault(subService, type);
375  		break;
376  	}
377  }
378  
379  // default callbacks do nothing
380  void ModuleEvents::insertion(uint32 subService, CSSM_SERVICE_TYPE type) { }
381  void ModuleEvents::removal(uint32 subService, CSSM_SERVICE_TYPE type) { }
382  void ModuleEvents::fault(uint32 subService, CSSM_SERVICE_TYPE type) { }
383  
384  
385  void
386  ModuleImpl::appNotifyCallback(CSSM_API_ModuleEventHandler appNotifyCallback, void *appNotifyCallbackCtx)
387  {
388  	secinfo("callback","In ModuleImpl::appNotifyCallback, appNotifyCallback=%p, appNotifyCallbackCtx=%p",
389  		appNotifyCallback, appNotifyCallbackCtx);
390  	if (mActive)
391  		Error::throwMe(Error::objectBusy);
392  
393  	mAppNotifyCallback = appNotifyCallback;
394  	mAppNotifyCallbackCtx = appNotifyCallbackCtx;
395  }
396  
397  void
398  ModuleImpl::appNotifyCallback(RawModuleEvents *handler)
399  {
400  	appNotifyCallback(RawModuleEvents::sendNotify, handler);
401  }
402  
403  void
404  ModuleImpl::activate()
405  {
406      {
407          StLock<Mutex> _(mActivateMutex);
408          if (!mActive)
409          {
410              session()->init();
411              // @@@ install handler here (use central dispatch with override)
412              secinfo("callback","In ModuleImpl::activate, mAppNotifyCallback=%p, mAppNotifyCallbackCtx=%p",
413                  mAppNotifyCallback, mAppNotifyCallbackCtx);
414              check(CSSM_ModuleLoad(&guid(), CSSM_KEY_HIERARCHY_NONE, mAppNotifyCallback, mAppNotifyCallbackCtx));
415              mActive = true;
416          }
417      }
418      
419      session()->catchExit();
420  }
421  
422  void
423  ModuleImpl::deactivate()
424  {
425  	if (!isIdle())
426  		Error::throwMe(Error::objectBusy);
427  
428      StLock<Mutex> _(mActivateMutex);
429  	if (mActive)
430  	{
431  		mActive = false;
432  		check(CSSM_ModuleUnload(&guid(), mAppNotifyCallback, mAppNotifyCallbackCtx));
433  	}
434  }
435  
436  Cssm
437  ModuleImpl::session() const
438  {
439  	return parent<Cssm>();
440  }
441  
442  
443  //
444  // CssmAttachment objects.
445  // parent ::= the loaded module object.
446  // active ::= attached.
447  //
448  AttachmentImpl::AttachmentImpl(const Guid &guid, CSSM_SERVICE_TYPE subserviceType)
449  : ObjectImpl(CssmImpl::standard()->autoModule(guid))
450  {
451  	make(subserviceType);
452  }
453  
454  AttachmentImpl::AttachmentImpl(const Module &module, CSSM_SERVICE_TYPE subserviceType)
455  : ObjectImpl(module)
456  {
457  	make(subserviceType);
458  }
459  
460  AttachmentImpl::~AttachmentImpl()
461  try
462  {
463  	detach();
464  }
465  catch (...) {
466      return;
467  }
468  
469  void
470  AttachmentImpl::make(CSSM_SERVICE_TYPE subserviceType)
471  {
472  	// default configuration
473  	mVersion.Major = 2;
474  	mVersion.Minor = 0;
475  	mSubserviceType = subserviceType;
476  	mSubserviceId = 0;
477  	mAttachFlags = 0;
478  }
479  
480  void
481  AttachmentImpl::activate()
482  {
483      StLock<Mutex> _(mActivateMutex);
484  	if (!mActive)
485  	{
486  		module()->load();
487  		mMemoryFunctions = CssmAllocatorMemoryFunctions(allocator());
488  		check(CSSM_ModuleAttach(&guid(), &mVersion,
489  			  &mMemoryFunctions,
490  			  mSubserviceId,
491  			  mSubserviceType,
492  			  mAttachFlags,
493  			  CSSM_KEY_HIERARCHY_NONE,
494  			  NULL, 0,	// no function pointer table return
495  			  NULL,		// reserved
496  			  &mHandle));
497  		mActive = true;
498  	}
499  }
500  
501  void
502  AttachmentImpl::deactivate()
503  {
504      StLock<Mutex> _(mActivateMutex);
505  	if (mActive)
506  	{
507  		mActive = false;
508  		check(CSSM_ModuleDetach(mHandle));
509  	}
510  }
511  
512  CSSM_SERVICE_MASK
513  AttachmentImpl::subserviceMask() const
514  {
515  	return mSubserviceType;
516  }
517  
518  void
519  AttachmentImpl::subserviceId(uint32 id)
520  {
521  	mSubserviceId = id;
522  }
523  
524  CssmSubserviceUid
525  AttachmentImpl::subserviceUid() const
526  {
527  	return CssmSubserviceUid(guid(), &mVersion, mSubserviceId, subserviceMask());
528  }
529  
530  Module
531  AttachmentImpl::module() const
532  {
533  	return parent<Module>();
534  }