--- /dev/null
+/*
+ * 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.
+ */
+
+
+//
+// SSCSPSession.cpp - Security Server CSP session.
+//
+#include "SSCSPSession.h"
+
+#include "CSPDLPlugin.h"
+#include "SSDatabase.h"
+#include "SSDLSession.h"
+#include "SSKey.h"
+#include <security_cdsa_utilities/cssmbridge.h>
+#include <memory>
+
+using namespace std;
+using namespace SecurityServer;
+
+//
+// SSCSPSession -- Security Server CSP session
+//
+SSCSPSession::SSCSPSession(CSSM_MODULE_HANDLE handle,
+ CSPDLPlugin &plug,
+ const CSSM_VERSION &version,
+ uint32 subserviceId,
+ CSSM_SERVICE_TYPE subserviceType,
+ CSSM_ATTACH_FLAGS attachFlags,
+ const CSSM_UPCALLS &upcalls,
+ SSCSPDLSession &ssCSPDLSession,
+ CssmClient::CSP &rawCsp)
+: CSPFullPluginSession(handle, plug, version, subserviceId, subserviceType,
+ attachFlags, upcalls),
+ mSSCSPDLSession(ssCSPDLSession),
+ mSSFactory(plug.mSSFactory),
+ mRawCsp(rawCsp),
+ mClientSession(Allocator::standard(), *this)
+{
+ mClientSession.registerForAclEdits(SSCSPDLSession::didChangeKeyAclCallback, &mSSCSPDLSession);
+}
+
+//
+// Called at (CSSM) context create time. This is ignored; we do a full
+// context setup later, at setupContext time.
+//
+CSPFullPluginSession::CSPContext *
+SSCSPSession::contextCreate(CSSM_CC_HANDLE handle, const Context &context)
+{
+ return NULL;
+}
+
+
+//
+// Called by CSPFullPluginSession when an op is actually commencing.
+// Context can safely assumed to be fully formed and stable for the
+// duration of the op; thus we wait until now to set up our
+// CSPContext as appropriate to the op.
+//
+void
+SSCSPSession::setupContext(CSPContext * &cspCtx,
+ const Context &context,
+ bool encoding)
+{
+ // note we skip this if this CSPContext is being reused
+ if (cspCtx == NULL)
+ {
+
+ if (mSSFactory.setup(*this, cspCtx, context, encoding))
+ return;
+
+#if 0
+ if (mBSafe4Factory.setup(*this, cspCtx, context))
+ return;
+
+ if (mCryptKitFactory.setup(*this, cspCtx, context))
+ return;
+#endif
+
+ CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
+ }
+}
+
+
+//
+// DL interaction
+//
+SSDatabase
+SSCSPSession::getDatabase(const Context &context)
+{
+ return getDatabase(context.get<CSSM_DL_DB_HANDLE>(CSSM_ATTRIBUTE_DL_DB_HANDLE));
+}
+
+SSDatabase
+SSCSPSession::getDatabase(CSSM_DL_DB_HANDLE *aDLDbHandle)
+{
+ if (aDLDbHandle)
+ return findSession<SSDLSession>(aDLDbHandle->DLHandle).findDbHandle(aDLDbHandle->DBHandle);
+ else
+ return SSDatabase();
+}
+
+
+//
+// Reference Key management
+//
+void
+SSCSPSession::makeReferenceKey(KeyHandle inKeyHandle, CssmKey &ioKey, SSDatabase &inSSDatabase,
+ uint32 inKeyAttr, const CssmData *inKeyLabel)
+{
+ return mSSCSPDLSession.makeReferenceKey(*this, inKeyHandle, ioKey, inSSDatabase, inKeyAttr, inKeyLabel);
+}
+
+SSKey &
+SSCSPSession::lookupKey(const CssmKey &inKey)
+{
+ return mSSCSPDLSession.lookupKey(inKey);
+}
+
+
+//
+// Key creating and handeling members
+//
+void
+SSCSPSession::WrapKey(CSSM_CC_HANDLE CCHandle,
+ const Context &context,
+ const AccessCredentials &AccessCred,
+ const CssmKey &Key,
+ const CssmData *DescriptiveData,
+ CssmKey &WrappedKey,
+ CSSM_PRIVILEGE Privilege)
+{
+ // @@@ Deal with permanent keys
+ const CssmKey *keyInContext =
+ context.get<const CssmKey>(CSSM_ATTRIBUTE_KEY);
+
+ KeyHandle contextKeyHandle = (keyInContext
+ ? lookupKey(*keyInContext).keyHandle()
+ : noKey);
+ clientSession().wrapKey(context, contextKeyHandle,
+ lookupKey(Key).keyHandle(), &AccessCred,
+ DescriptiveData, WrappedKey, *this);
+}
+
+void
+SSCSPSession::UnwrapKey(CSSM_CC_HANDLE CCHandle,
+ const Context &context,
+ const CssmKey *PublicKey,
+ const CssmWrappedKey &WrappedKey,
+ uint32 KeyUsage,
+ uint32 KeyAttr,
+ const CssmData *KeyLabel,
+ const CSSM_RESOURCE_CONTROL_CONTEXT *CredAndAclEntry,
+ CssmKey &UnwrappedKey,
+ CssmData &DescriptiveData,
+ CSSM_PRIVILEGE Privilege)
+{
+ SSDatabase database = getDatabase(context);
+ validateKeyAttr(KeyAttr);
+ const AccessCredentials *cred = NULL;
+ const AclEntryInput *owner = NULL;
+ if (CredAndAclEntry)
+ {
+ cred = AccessCredentials::overlay(CredAndAclEntry->AccessCred);
+ owner = &AclEntryInput::overlay(CredAndAclEntry->InitialAclEntry);
+ }
+
+ KeyHandle publicKey = noKey;
+ if (PublicKey)
+ {
+ if (PublicKey->blobType() == CSSM_KEYBLOB_RAW)
+ {
+ // @@@ We need to unwrap the publicKey into the SecurityServer
+ // before continuing
+ CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
+ }
+ else
+ publicKey = lookupKey(*PublicKey).keyHandle();
+ }
+
+ // @@@ Deal with permanent keys
+ const CssmKey *keyInContext =
+ context.get<const CssmKey>(CSSM_ATTRIBUTE_KEY);
+
+ KeyHandle contextKeyHandle =
+ keyInContext ? lookupKey(*keyInContext).keyHandle() : noKey;
+
+ KeyHandle unwrappedKeyHandle;
+ clientSession().unwrapKey(database.dbHandle(), context, contextKeyHandle,
+ publicKey, WrappedKey, KeyUsage, KeyAttr,
+ cred, owner, DescriptiveData, unwrappedKeyHandle,
+ UnwrappedKey.header(), *this);
+ makeReferenceKey(unwrappedKeyHandle, UnwrappedKey, database, KeyAttr,
+ KeyLabel);
+}
+
+void
+SSCSPSession::DeriveKey(CSSM_CC_HANDLE ccHandle,
+ const Context &context,
+ CssmData ¶m,
+ uint32 keyUsage,
+ uint32 keyAttr,
+ const CssmData *keyLabel,
+ const CSSM_RESOURCE_CONTROL_CONTEXT *credAndAclEntry,
+ CssmKey &derivedKey)
+{
+ SSDatabase database = getDatabase(context);
+ validateKeyAttr(keyAttr);
+ const AccessCredentials *cred = NULL;
+ const AclEntryInput *owner = NULL;
+ if (credAndAclEntry)
+ {
+ cred = AccessCredentials::overlay(credAndAclEntry->AccessCred);
+ owner = &AclEntryInput::overlay(credAndAclEntry->InitialAclEntry);
+ }
+
+ /* optional BaseKey */
+ const CssmKey *keyInContext =
+ context.get<const CssmKey>(CSSM_ATTRIBUTE_KEY);
+ KeyHandle contextKeyHandle =
+ keyInContext ? lookupKey(*keyInContext).keyHandle() : noKey;
+ KeyHandle keyHandle;
+ switch(context.algorithm()) {
+ case CSSM_ALGID_KEYCHAIN_KEY:
+ {
+ // special interpretation: take DLDBHandle -> DbHandle from params
+ clientSession().extractMasterKey(database.dbHandle(), context,
+ getDatabase(param.interpretedAs<CSSM_DL_DB_HANDLE>(CSSMERR_CSP_INVALID_ATTR_DL_DB_HANDLE)).dbHandle(),
+ keyUsage, keyAttr, cred, owner, keyHandle, derivedKey.header());
+ }
+ break;
+ default:
+ clientSession().deriveKey(database.dbHandle(), context, contextKeyHandle, keyUsage,
+ keyAttr, param, cred, owner, keyHandle, derivedKey.header());
+ break;
+ }
+ makeReferenceKey(keyHandle, derivedKey, database, keyAttr, keyLabel);
+}
+
+void
+SSCSPSession::GenerateKey(CSSM_CC_HANDLE ccHandle,
+ const Context &context,
+ uint32 keyUsage,
+ uint32 keyAttr,
+ const CssmData *keyLabel,
+ const CSSM_RESOURCE_CONTROL_CONTEXT *credAndAclEntry,
+ CssmKey &key,
+ CSSM_PRIVILEGE privilege)
+{
+ SSDatabase database = getDatabase(context);
+ validateKeyAttr(keyAttr);
+ const AccessCredentials *cred = NULL;
+ const AclEntryInput *owner = NULL;
+ if (credAndAclEntry)
+ {
+ cred = AccessCredentials::overlay(credAndAclEntry->AccessCred);
+ owner = &AclEntryInput::overlay(credAndAclEntry->InitialAclEntry);
+ }
+
+ KeyHandle keyHandle;
+ clientSession().generateKey(database.dbHandle(), context, keyUsage,
+ keyAttr, cred, owner, keyHandle, key.header());
+ makeReferenceKey(keyHandle, key, database, keyAttr, keyLabel);
+}
+
+void
+SSCSPSession::GenerateKeyPair(CSSM_CC_HANDLE ccHandle,
+ const Context &context,
+ uint32 publicKeyUsage,
+ uint32 publicKeyAttr,
+ const CssmData *publicKeyLabel,
+ CssmKey &publicKey,
+ uint32 privateKeyUsage,
+ uint32 privateKeyAttr,
+ const CssmData *privateKeyLabel,
+ const CSSM_RESOURCE_CONTROL_CONTEXT *credAndAclEntry,
+ CssmKey &privateKey,
+ CSSM_PRIVILEGE privilege)
+{
+ SSDatabase database = getDatabase(context);
+ validateKeyAttr(publicKeyAttr);
+ validateKeyAttr(privateKeyAttr);
+ const AccessCredentials *cred = NULL;
+ const AclEntryInput *owner = NULL;
+ if (credAndAclEntry)
+ {
+ cred = AccessCredentials::overlay(credAndAclEntry->AccessCred);
+ owner = &AclEntryInput::overlay(credAndAclEntry->InitialAclEntry);
+ }
+
+ /*
+ * Public keys must be extractable in the clear - that's the Apple
+ * policy. The raw CSP is unable to enforce the extractable
+ * bit since it always sees that as true (it's managed and forced
+ * true by the SecurityServer). So...
+ */
+ if(!(publicKeyAttr & CSSM_KEYATTR_EXTRACTABLE)) {
+ CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
+ }
+ KeyHandle pubKeyHandle, privKeyHandle;
+ clientSession().generateKey(database.dbHandle(), context,
+ publicKeyUsage, publicKeyAttr,
+ privateKeyUsage, privateKeyAttr,
+ cred, owner,
+ pubKeyHandle, publicKey.header(),
+ privKeyHandle, privateKey.header());
+ makeReferenceKey(privKeyHandle, privateKey, database, privateKeyAttr,
+ privateKeyLabel);
+ // @@@ What if this throws, we need to free privateKey.
+ makeReferenceKey(pubKeyHandle, publicKey, database, publicKeyAttr,
+ publicKeyLabel);
+}
+
+void
+SSCSPSession::ObtainPrivateKeyFromPublicKey(const CssmKey &PublicKey,
+ CssmKey &PrivateKey)
+{
+ unimplemented();
+}
+
+void
+SSCSPSession::QueryKeySizeInBits(CSSM_CC_HANDLE CCHandle,
+ const Context &Context,
+ const CssmKey &Key,
+ CSSM_KEY_SIZE &KeySize)
+{
+ unimplemented();
+}
+
+void
+SSCSPSession::FreeKey(const AccessCredentials *accessCred,
+ CssmKey &ioKey, CSSM_BOOL deleteKey)
+{
+ if (ioKey.blobType() == CSSM_KEYBLOB_REFERENCE)
+ {
+ // @@@ Note that this means that detaching a session should free
+ // all keys ascociated with it or else...
+ // -- or else what?
+ // exactly!
+
+ // @@@ There are thread safety issues when deleting a key that is
+ // in use by another thread, but the answer to that is: Don't do
+ // that!
+
+ // Find the key in the map. Tell tell the key to free itself
+ // (when the auto_ptr deletes the key it removes itself from the map).
+ secdebug("freeKey", "CSPDL FreeKey");
+ auto_ptr<SSKey> ssKey(&mSSCSPDLSession.find<SSKey>(ioKey));
+ ssKey->free(accessCred, ioKey, deleteKey);
+ }
+ else
+ {
+ CSPFullPluginSession::FreeKey(accessCred, ioKey, deleteKey);
+ }
+}
+
+
+//
+// Generation stuff.
+//
+void
+SSCSPSession::GenerateRandom(CSSM_CC_HANDLE ccHandle,
+ const Context &context,
+ CssmData &randomNumber)
+{
+ checkOperation(context.type(), CSSM_ALGCLASS_RANDOMGEN);
+ // if (context.algorithm() != @@@) CssmError::throwMe(ALGORITHM_NOT_SUPPORTED);
+ uint32 needed = context.getInt(CSSM_ATTRIBUTE_OUTPUT_SIZE, CSSMERR_CSP_MISSING_ATTR_OUTPUT_SIZE);
+
+ // @@@ What about the seed?
+ if (randomNumber.length())
+ {
+ if (randomNumber.length() < needed)
+ CssmError::throwMe(CSSMERR_CSP_OUTPUT_LENGTH_ERROR);
+ clientSession().generateRandom(context, randomNumber);
+ }
+ else
+ {
+ randomNumber.Data = alloc<uint8>(needed);
+ try
+ {
+ clientSession().generateRandom(context, randomNumber);
+ }
+ catch(...)
+ {
+ free(randomNumber.Data);
+ randomNumber.Data = NULL;
+ throw;
+ }
+ }
+}
+
+//
+// Login/Logout and token operational maintainance. These mean little
+// without support by the actual implementation, but we can help...
+// @@@ Should this be in CSP[non-Full]PluginSession?
+//
+void
+SSCSPSession::Login(const AccessCredentials &AccessCred,
+ const CssmData *LoginName,
+ const void *Reserved)
+{
+ // @@@ Do a login to the securityServer making keys persistant until it
+ // goes away
+ unimplemented();
+}
+
+void
+SSCSPSession::Logout()
+{
+ unimplemented();
+}
+
+void
+SSCSPSession::VerifyDevice(const CssmData &DeviceCert)
+{
+ CssmError::throwMe(CSSMERR_CSP_DEVICE_VERIFY_FAILED);
+}
+
+void
+SSCSPSession::GetOperationalStatistics(CSPOperationalStatistics &statistics)
+{
+ unimplemented();
+}
+
+
+//
+// Utterly miscellaneous, rarely used, strange functions
+//
+void
+SSCSPSession::RetrieveCounter(CssmData &Counter)
+{
+ unimplemented();
+}
+
+void
+SSCSPSession::RetrieveUniqueId(CssmData &UniqueID)
+{
+ unimplemented();
+}
+
+void
+SSCSPSession::GetTimeValue(CSSM_ALGORITHMS TimeAlgorithm, CssmData &TimeData)
+{
+ unimplemented();
+}
+
+
+//
+// ACL retrieval and change operations
+//
+void
+SSCSPSession::GetKeyOwner(const CssmKey &Key,
+ CSSM_ACL_OWNER_PROTOTYPE &Owner)
+{
+ lookupKey(Key).getOwner(Owner, *this);
+}
+
+void
+SSCSPSession::ChangeKeyOwner(const AccessCredentials &AccessCred,
+ const CssmKey &Key,
+ const CSSM_ACL_OWNER_PROTOTYPE &NewOwner)
+{
+ lookupKey(Key).changeOwner(AccessCred,
+ AclOwnerPrototype::overlay(NewOwner));
+}
+
+void
+SSCSPSession::GetKeyAcl(const CssmKey &Key,
+ const CSSM_STRING *SelectionTag,
+ uint32 &NumberOfAclInfos,
+ CSSM_ACL_ENTRY_INFO_PTR &AclInfos)
+{
+ lookupKey(Key).getAcl(reinterpret_cast<const char *>(SelectionTag),
+ NumberOfAclInfos,
+ reinterpret_cast<AclEntryInfo *&>(AclInfos), *this);
+}
+
+void
+SSCSPSession::ChangeKeyAcl(const AccessCredentials &AccessCred,
+ const CSSM_ACL_EDIT &AclEdit,
+ const CssmKey &Key)
+{
+ lookupKey(Key).changeAcl(AccessCred, AclEdit::overlay(AclEdit));
+}
+
+void
+SSCSPSession::GetLoginOwner(CSSM_ACL_OWNER_PROTOTYPE &Owner)
+{
+ unimplemented();
+}
+
+void
+SSCSPSession::ChangeLoginOwner(const AccessCredentials &AccessCred,
+ const CSSM_ACL_OWNER_PROTOTYPE &NewOwner)
+{
+ unimplemented();
+}
+
+void
+SSCSPSession::GetLoginAcl(const CSSM_STRING *SelectionTag,
+ uint32 &NumberOfAclInfos,
+ CSSM_ACL_ENTRY_INFO_PTR &AclInfos)
+{
+ unimplemented();
+}
+
+void
+SSCSPSession::ChangeLoginAcl(const AccessCredentials &AccessCred,
+ const CSSM_ACL_EDIT &AclEdit)
+{
+ unimplemented();
+}
+
+
+
+//
+// Passthroughs
+//
+void
+SSCSPSession::PassThrough(CSSM_CC_HANDLE CCHandle,
+ const Context &context,
+ uint32 passThroughId,
+ const void *inData,
+ void **outData)
+{
+ checkOperation(context.type(), CSSM_ALGCLASS_NONE);
+ switch (passThroughId) {
+ case CSSM_APPLESCPDL_CSP_GET_KEYHANDLE:
+ {
+ // inData unused, must be NULL
+ if (inData)
+ CssmError::throwMe(CSSM_ERRCODE_INVALID_INPUT_POINTER);
+
+ // outData required, must be pointer-to-pointer-to-KeyHandle
+ KeyHandle &result = Required(reinterpret_cast<KeyHandle *>(outData));
+
+ // we'll take the key from the context
+ const CssmKey &key =
+ context.get<const CssmKey>(CSSM_ATTRIBUTE_KEY, CSSMERR_CSP_MISSING_ATTR_KEY);
+
+ // all ready
+ result = lookupKey(key).keyHandle();
+ break;
+ }
+ case CSSM_APPLECSP_KEYDIGEST:
+ {
+ // inData unused, must be NULL
+ if (inData)
+ CssmError::throwMe(CSSM_ERRCODE_INVALID_INPUT_POINTER);
+
+ // outData required
+ Required(outData);
+
+ // take the key from the context, convert to KeyHandle
+ const CssmKey &key =
+ context.get<const CssmKey>(CSSM_ATTRIBUTE_KEY, CSSMERR_CSP_MISSING_ATTR_KEY);
+ KeyHandle keyHandle = lookupKey(key).keyHandle();
+
+ // allocate digest holder on app's behalf
+ CSSM_DATA *digest = alloc<CSSM_DATA>(sizeof(CSSM_DATA));
+ digest->Data = NULL;
+ digest->Length = 0;
+
+ // go
+ try {
+ clientSession().getKeyDigest(keyHandle, CssmData::overlay(*digest));
+ }
+ catch(...) {
+ free(digest);
+ throw;
+ }
+ *outData = digest;
+ break;
+ }
+
+ default:
+ CssmError::throwMe(CSSM_ERRCODE_INVALID_PASSTHROUGH_ID);
+ }
+}
+
+/* Validate requested key attr flags for newly generated keys */
+void SSCSPSession::validateKeyAttr(uint32 reqKeyAttr)
+{
+ if(reqKeyAttr & (CSSM_KEYATTR_RETURN_DATA)) {
+ /* CSPDL only supports reference keys */
+ CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYATTR_MASK);
+ }
+ if(reqKeyAttr & (CSSM_KEYATTR_ALWAYS_SENSITIVE |
+ CSSM_KEYATTR_NEVER_EXTRACTABLE)) {
+ /* invalid for any CSP */
+ CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
+ }
+ /* There may be more, but we'll leave it to SS and CSP to decide */
+}