cssmclient.cpp   [plain text]


/*
 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
 * 
 * The contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License. Please obtain
 * a copy of the License at http://www.apple.com/publicsource and read it before
 * using this file.
 * 
 * This 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.
 */


//
// cssmclient - common client interface to CSSM and MDS.
//
// Locking Strategy (preliminary):
// XXX This is obsolete update this --mb
// A CssmObject is a CountingMutex. Its count represents the number of children that have registered
// themselves (using addChild/removeChild). The lock controls the internal management fields of the
// various subclasses to protect them against corruption. It does NOT control attribute and argument
// fields and operations, not does it control object-constant fields.
// This means that if you use an object from multiple threads, you (the caller) must lock the object
// during set/get calls of attributes. Note that the CSSM operations themselves are safely multithreaded
// and thus don't need to be interlocked explicitly.
//
#include <security_cdsa_client/cssmclient.h>


using namespace CssmClient;

//
// Exception model
//
const char *
Error::what () const throw()
{
	return "CSSM client library error";
}


//
// General utilities
//
void
ObjectImpl::check(CSSM_RETURN status)
{
	if (status != CSSM_OK)
	{
		CssmError::throwMe(status);
	}
}


//
// Common features of Objects
//
ObjectImpl::ObjectImpl() : mParent(), mChildCount(0)
{
	mActive = false;		// not activated
	mAllocator = NULL;		// allocator to be determined
}

ObjectImpl::ObjectImpl(const Object &mommy) : mParent(mommy.mImpl), mChildCount(0)
{
	mActive = false;		// not activated
	mAllocator = NULL;		// allocator to be determined
	if (mParent)
		mParent->addChild();
}

ObjectImpl::~ObjectImpl()
try
{
	assert(!mActive);	// subclass must have deactivated us
	assert(isIdle());
		
	// release parent from her obligations (if we still have one)
	if (mParent)
		mParent->removeChild();
}
catch(...)
{
	return;
}

void
ObjectImpl::addChild()
{
	mChildCount++;		// atomic
}

void
ObjectImpl::removeChild()
{
	mChildCount--;		// atomic
}


//
// Manage allocators in the Object tree
//
Allocator &
ObjectImpl::allocator() const
{
	if (mAllocator == NULL)
	{
		// fix allocator now
		if (mParent)
			mAllocator = &mParent->allocator();
		else
			mAllocator = &Allocator::standard();
	}

	return *mAllocator;
}

void
ObjectImpl::allocator(Allocator &alloc)
{
	assert(mAllocator == NULL);	// cannot redefine allocator once set
	mAllocator = &alloc;
}

// Comparison operators use pointer comparison by default.  Subclasses may override.
bool
ObjectImpl::operator <(const ObjectImpl &other) const
{
	return this < &other;
}

bool
ObjectImpl::operator ==(const ObjectImpl &other) const
{
	return this == &other;
}


//
// CSSMSession objects.
// parent ::= NULL (none)
// active ::= CSSM initialized
//
ModuleNexus<CssmImpl::StandardCssm> CssmImpl::mStandard;

CssmImpl::CssmImpl() : ObjectImpl()
{
	setup();
	mStandard().setCssm(this);
}

CssmImpl::CssmImpl(bool) : ObjectImpl()
{
	setup();
	// implicitly constructed - caller responsible for standard session management
}

CssmImpl::~CssmImpl()
{
    try
    {
		deactivate();
    }
	catch(...) {}

	// this may be the standard session...
    mStandard().unsetCssm(this);
}


void
CssmImpl::setup()
{
	// set default configuration
	mVersion.Major = 2;
	mVersion.Minor = 0;
	mScope = CSSM_PRIVILEGE_SCOPE_PROCESS;
}


Cssm
CssmImpl::standard()
{
    return Cssm(mStandard().get());
}


void
CssmImpl::activate()
{
	if (!mActive)
	{
		// currently, no choices on PVC mode and key hierarchy
		CSSM_PVC_MODE pvc = CSSM_PVC_NONE;
		switch (CSSM_RETURN rc = CSSM_Init(&mVersion,
				mScope, &mCallerGuid,
				CSSM_KEY_HIERARCHY_NONE, &pvc, NULL)) {
		case CSSMERR_CSSM_PVC_ALREADY_CONFIGURED:
		case CSSM_OK:
			break;
		default:
			check(rc);
		}
		mActive = true;
	}
}

void
CssmImpl::deactivate()
{
	if (mActive)
	{
		mActive = false;

		// clear module map (all gone now)
		moduleMap.erase(moduleMap.begin(), moduleMap.end());
	
		// now terminate CSSM
		check(CSSM_Terminate());
	}
}

void
CssmImpl::atExitHandler()
{
    try {
        mStandard.reset();
    } catch (...) {
    }
}

void
CssmImpl::catchExit()
{
	// @@@ Even though this is the "right thing" to do.  This only causes
	// exceptions during exit and doesn't really help cleanup correctly.
#if 0
	if (::atexit(atExitHandler))
		UnixError::throwMe();
#endif
}


//
// Manage the automatic Cssm object.
// This is a program global.
//
void CssmImpl::StandardCssm::setCssm(CssmImpl *cssm)
{
    StLock<Mutex> _(*this);
    if (mCssm == NULL)
        mCssm = cssm;
}

void CssmImpl::StandardCssm::unsetCssm(CssmImpl *cssm)
{
    StLock<Mutex> _(*this);
    if (mCssm == cssm)
        mCssm = NULL;
}

CssmImpl *CssmImpl::StandardCssm::get()
{
    StLock<Mutex> _(*this);
    if (mCssm == NULL) {	// make the default instance
        mCssm = new CssmImpl(true);
    }
    return mCssm;
}

CssmImpl::StandardCssm::~StandardCssm()
{
    if (mCssm) {
        mCssm->deactivate();
        delete mCssm;
    }
}


//
// Auto-module management
//
Module
CssmImpl::autoModule(const Guid &guid)
{
	StLock<Mutex> _(mapLock);
	ModuleMap::iterator it = moduleMap.find(guid);
	if (it == moduleMap.end())
	{
		// no automodule for this guid yet, create one
		Module module(guid, Cssm(this));
		moduleMap.insert(ModuleMap::value_type(guid, module));
		return module;
	}
	else
	{
		// existing automodule - use it
		return it->second;
	}
}


//
// Module objects.
// parent ::= the session object (usually Cssm::standard)
// active ::= module is loaded.
//
ModuleImpl::ModuleImpl(const Guid &guid) : ObjectImpl(Cssm::standard()),
	mAppNotifyCallback(NULL),
	mAppNotifyCallbackCtx(NULL)
{
	setGuid(guid);
}

ModuleImpl::ModuleImpl(const Guid &guid, const Cssm &session) : ObjectImpl(session),
	mAppNotifyCallback(NULL),
	mAppNotifyCallbackCtx(NULL)
{
	setGuid(guid);
}

ModuleImpl::~ModuleImpl()
{
	unload();
}


//
// RawModuleEvent objects encapsulate CSSM module callbacks
//
RawModuleEvents::~RawModuleEvents()
{ }

CSSM_RETURN RawModuleEvents::sendNotify(const CSSM_GUID *, void *context,
	uint32 subService, CSSM_SERVICE_TYPE type, CSSM_MODULE_EVENT event)
{
	try {
		reinterpret_cast<RawModuleEvents *>(context)->notify(subService, type, event);
		return CSSM_OK;
	} catch (const CommonError &error) {
		return CssmError::cssmError(error, CSSM_CSSM_BASE_ERROR);
	} catch (...) {
		return CSSMERR_CSSM_INTERNAL_ERROR;	// whatever...
	}
}


//
// ModuleEvents enhance RawModuleEvents by splitting the callback up by type
//
void ModuleEvents::notify(uint32 subService,
	CSSM_SERVICE_TYPE type, CSSM_MODULE_EVENT event)
{
	switch (event) {
	case CSSM_NOTIFY_INSERT:
		insertion(subService, type);
		break;
	case CSSM_NOTIFY_REMOVE:
		removal(subService, type);
		break;
	case CSSM_NOTIFY_FAULT:
		fault(subService, type);
		break;
	}
}

// default callbacks do nothing
void ModuleEvents::insertion(uint32 subService, CSSM_SERVICE_TYPE type) { }
void ModuleEvents::removal(uint32 subService, CSSM_SERVICE_TYPE type) { }
void ModuleEvents::fault(uint32 subService, CSSM_SERVICE_TYPE type) { }


void
ModuleImpl::appNotifyCallback(CSSM_API_ModuleEventHandler appNotifyCallback, void *appNotifyCallbackCtx)
{
	secdebug("callback","In ModuleImpl::appNotifyCallback, appNotifyCallback=%p, appNotifyCallbackCtx=%p",
		appNotifyCallback, appNotifyCallbackCtx);
	if (mActive)
		Error::throwMe(Error::objectBusy);

	mAppNotifyCallback = appNotifyCallback;
	mAppNotifyCallbackCtx = appNotifyCallbackCtx;
}

void
ModuleImpl::appNotifyCallback(RawModuleEvents *handler)
{
	appNotifyCallback(RawModuleEvents::sendNotify, handler);
}

void
ModuleImpl::activate()
{
	if (!mActive)
	{
		session()->init();
		// @@@ install handler here (use central dispatch with override)
		secdebug("callback","In ModuleImpl::activate, mAppNotifyCallback=%p, mAppNotifyCallbackCtx=%p",
			mAppNotifyCallback, mAppNotifyCallbackCtx);
		check(CSSM_ModuleLoad(&guid(), CSSM_KEY_HIERARCHY_NONE, mAppNotifyCallback, mAppNotifyCallbackCtx));
		mActive = true;
		session()->catchExit();
	}
}

void
ModuleImpl::deactivate()
{
	if (!isIdle())
		Error::throwMe(Error::objectBusy);
	if (mActive)
	{
		mActive = false;
		check(CSSM_ModuleUnload(&guid(), mAppNotifyCallback, mAppNotifyCallbackCtx));
	}
}

Cssm
ModuleImpl::session() const
{
	return parent<Cssm>();
}


//
// CssmAttachment objects.
// parent ::= the loaded module object.
// active ::= attached.
//
AttachmentImpl::AttachmentImpl(const Guid &guid, CSSM_SERVICE_TYPE subserviceType)
: ObjectImpl(CssmImpl::standard()->autoModule(guid))
{
	make(subserviceType);
}

AttachmentImpl::AttachmentImpl(const Module &module, CSSM_SERVICE_TYPE subserviceType)
: ObjectImpl(module)
{
	make(subserviceType);
}

AttachmentImpl::~AttachmentImpl()
{
	detach();
}

void
AttachmentImpl::make(CSSM_SERVICE_TYPE subserviceType)
{
	// default configuration
	mVersion.Major = 2;
	mVersion.Minor = 0;
	mSubserviceType = subserviceType;
	mSubserviceId = 0;
	mAttachFlags = 0;
}

void
AttachmentImpl::activate()
{
	if (!mActive)
	{
		module()->load();
		mMemoryFunctions = CssmAllocatorMemoryFunctions(allocator());
		check(CSSM_ModuleAttach(&guid(), &mVersion,
			  &mMemoryFunctions,
			  mSubserviceId,
			  mSubserviceType,
			  mAttachFlags,
			  CSSM_KEY_HIERARCHY_NONE,
			  NULL, 0,	// no function pointer table return
			  NULL,		// reserved
			  &mHandle));
		mActive = true;
	}
}

void
AttachmentImpl::deactivate()
{
	if (mActive)
	{
		mActive = false;
		check(CSSM_ModuleDetach(mHandle));
	}
}

CSSM_SERVICE_MASK
AttachmentImpl::subserviceMask() const
{
	return mSubserviceType;
}

void
AttachmentImpl::subserviceId(uint32 id)
{
	mSubserviceId = id;
}

CssmSubserviceUid
AttachmentImpl::subserviceUid() const
{
	return CssmSubserviceUid(guid(), &mVersion, mSubserviceId, subserviceMask());
}

Module
AttachmentImpl::module() const
{
	return parent<Module>();
}