/* * 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 #include using namespace CssmClient; // // Exception model // CSSM_RETURN Error::cssmError() const { //@@@ munge in client-side error codes here? return CssmError::cssmError(); } 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() { assert(!mActive); // subclass must have deactivated us if (!isIdle()) Error::throwMe(Error::objectBusy); // release parent from her obligations (if we still have one) if (mParent) mParent->removeChild(); } void ObjectImpl::addChild() { mChildCount++; // atomic } void ObjectImpl::removeChild() { mChildCount--; // atomic } // // Manage allocators in the Object tree // CssmAllocator & ObjectImpl::allocator() const { if (mAllocator == NULL) { // fix allocator now if (mParent) mAllocator = &mParent->allocator(); else mAllocator = &CssmAllocator::standard(); } return *mAllocator; } void ObjectImpl::allocator(CssmAllocator &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::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 _(*this); if (mCssm == NULL) mCssm = cssm; } void CssmImpl::StandardCssm::unsetCssm(CssmImpl *cssm) { StLock _(*this); if (mCssm == cssm) mCssm = NULL; } CssmImpl *CssmImpl::StandardCssm::get() { StLock _(*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 _(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()) { setGuid(guid); } ModuleImpl::ModuleImpl(const Guid &guid, const Cssm &session) : ObjectImpl(session) { setGuid(guid); } ModuleImpl::~ModuleImpl() { unload(); } void ModuleImpl::activate() { if (!mActive) { session()->init(); // @@@ install handler here (use central dispatch with override) check(CSSM_ModuleLoad(&guid(), CSSM_KEY_HIERARCHY_NONE, NULL, NULL)); mActive = true; session()->catchExit(); } } void ModuleImpl::deactivate() { if (!isIdle()) Error::throwMe(Error::objectBusy); if (mActive) { mActive = false; check(CSSM_ModuleUnload(&guid(), NULL, NULL)); } } Cssm ModuleImpl::session() const { return parent(); } // // 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(); }