]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_cdsa_client/lib/cssmclient.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_cdsa_client / lib / cssmclient.cpp
diff --git a/libsecurity_cdsa_client/lib/cssmclient.cpp b/libsecurity_cdsa_client/lib/cssmclient.cpp
new file mode 100644 (file)
index 0000000..ec3fb44
--- /dev/null
@@ -0,0 +1,515 @@
+/*
+ * 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>();
+}