X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_sd_cspdl/lib/SDCSPSession.cpp diff --git a/Security/libsecurity_sd_cspdl/lib/SDCSPSession.cpp b/Security/libsecurity_sd_cspdl/lib/SDCSPSession.cpp new file mode 100644 index 00000000..3e62d12d --- /dev/null +++ b/Security/libsecurity_sd_cspdl/lib/SDCSPSession.cpp @@ -0,0 +1,612 @@ +/* + * Copyright (c) 2004,2008,2011-2012 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// +// SDCSPSession.cpp - Security Server CSP session. +// +#include "SDCSPSession.h" + +#include "SDCSPDLPlugin.h" +#include "SDDLSession.h" +#include "SDKey.h" +#include +#include + +using namespace std; +using namespace SecurityServer; + +// +// SDCSPSession -- Security Server CSP session +// +SDCSPSession::SDCSPSession(CSSM_MODULE_HANDLE handle, + SDCSPDLPlugin &plug, + const CSSM_VERSION &version, + uint32 subserviceId, + CSSM_SERVICE_TYPE subserviceType, + CSSM_ATTACH_FLAGS attachFlags, + const CSSM_UPCALLS &upcalls, + SDCSPDLSession &ssCSPDLSession, + CssmClient::CSP &rawCsp) +: CSPFullPluginSession(handle, plug, version, subserviceId, subserviceType, + attachFlags, upcalls), + mSDCSPDLSession(ssCSPDLSession), + mSDFactory(plug.mSDFactory), + mRawCsp(rawCsp), + mClientSession(Allocator::standard(), *this) +{ +} + +// +// Called at (CSSM) context create time. This is ignored; we do a full +// context setup later, at setupContext time. +// +CSPFullPluginSession::CSPContext * +SDCSPSession::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 +SDCSPSession::setupContext(CSPContext * &cspCtx, + const Context &context, + bool encoding) +{ + // note we skip this if this CSPContext is being reused + if (cspCtx == NULL) + { + + if (mSDFactory.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 +// +CSSM_DB_HANDLE +SDCSPSession::getDatabase(const Context &context) +{ + return getDatabase(context.get(CSSM_ATTRIBUTE_DL_DB_HANDLE)); +} + +CSSM_DB_HANDLE +SDCSPSession::getDatabase(CSSM_DL_DB_HANDLE *aDLDbHandle) +{ + if (aDLDbHandle) + return aDLDbHandle->DBHandle; + else + return CSSM_INVALID_HANDLE; +} + + +// +// Reference Key management +// +void +SDCSPSession::makeReferenceKey(KeyHandle inKeyHandle, CssmKey &ioKey, CSSM_DB_HANDLE inDBHandle, + uint32 inKeyAttr, const CssmData *inKeyLabel) +{ + return mSDCSPDLSession.makeReferenceKey(*this, inKeyHandle, ioKey, inDBHandle, inKeyAttr, inKeyLabel); +} + +SDKey & +SDCSPSession::lookupKey(const CssmKey &inKey) +{ + return mSDCSPDLSession.lookupKey(inKey); +} + + +// +// Key creating and handeling members +// +void +SDCSPSession::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(CSSM_ATTRIBUTE_KEY); + + KeyHandle contextKeyHandle = (keyInContext + ? lookupKey(*keyInContext).keyHandle() + : noKey); + clientSession().wrapKey(context, contextKeyHandle, + lookupKey(Key).keyHandle(), &AccessCred, + DescriptiveData, WrappedKey, *this); +} + +void +SDCSPSession::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) +{ + CSSM_DB_HANDLE 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(CSSM_ATTRIBUTE_KEY); + + KeyHandle contextKeyHandle = + keyInContext ? lookupKey(*keyInContext).keyHandle() : noKey; + + KeyHandle unwrappedKeyHandle; + clientSession().unwrapKey(ClientSession::toIPCHandle(database), context, contextKeyHandle, + publicKey, WrappedKey, KeyUsage, KeyAttr, + cred, owner, DescriptiveData, unwrappedKeyHandle, + UnwrappedKey.header(), *this); + makeReferenceKey(unwrappedKeyHandle, UnwrappedKey, database, KeyAttr, + KeyLabel); +} + +void +SDCSPSession::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) +{ + CSSM_DB_HANDLE 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(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(ClientSession::toIPCHandle(database), context, + (DbHandle)getDatabase(param.interpretedAs(CSSMERR_CSP_INVALID_ATTR_DL_DB_HANDLE)), + keyUsage, keyAttr, cred, owner, keyHandle, derivedKey.header()); + } + break; + default: + clientSession().deriveKey(ClientSession::toIPCHandle(database), context, contextKeyHandle, keyUsage, + keyAttr, param, cred, owner, keyHandle, derivedKey.header()); + break; + } + makeReferenceKey(keyHandle, derivedKey, database, keyAttr, keyLabel); +} + +void +SDCSPSession::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) +{ + CSSM_DB_HANDLE 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(ClientSession::toIPCHandle(database), context, keyUsage, + keyAttr, cred, owner, keyHandle, key.header()); + makeReferenceKey(keyHandle, key, database, keyAttr, keyLabel); +} + +void +SDCSPSession::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) +{ + CSSM_DB_HANDLE 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(ClientSession::toIPCHandle(database), 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 +SDCSPSession::ObtainPrivateKeyFromPublicKey(const CssmKey &PublicKey, + CssmKey &PrivateKey) +{ + unimplemented(); +} + +void +SDCSPSession::QueryKeySizeInBits(CSSM_CC_HANDLE CCHandle, + const Context &Context, + const CssmKey &Key, + CSSM_KEY_SIZE &KeySize) +{ + unimplemented(); +} + +void +SDCSPSession::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(&mSDCSPDLSession.find(ioKey)); + ssKey->free(accessCred, ioKey, deleteKey); + } + else + { + CSPFullPluginSession::FreeKey(accessCred, ioKey, deleteKey); + } +} + + +// +// Generation stuff. +// +void +SDCSPSession::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(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 +SDCSPSession::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 +SDCSPSession::Logout() +{ + unimplemented(); +} + +void +SDCSPSession::VerifyDevice(const CssmData &DeviceCert) +{ + CssmError::throwMe(CSSMERR_CSP_DEVICE_VERIFY_FAILED); +} + +void +SDCSPSession::GetOperationalStatistics(CSPOperationalStatistics &statistics) +{ + unimplemented(); +} + + +// +// Utterly miscellaneous, rarely used, strange functions +// +void +SDCSPSession::RetrieveCounter(CssmData &Counter) +{ + unimplemented(); +} + +void +SDCSPSession::RetrieveUniqueId(CssmData &UniqueID) +{ + unimplemented(); +} + +void +SDCSPSession::GetTimeValue(CSSM_ALGORITHMS TimeAlgorithm, CssmData &TimeData) +{ + unimplemented(); +} + + +// +// ACL retrieval and change operations +// +void +SDCSPSession::GetKeyOwner(const CssmKey &Key, + CSSM_ACL_OWNER_PROTOTYPE &Owner) +{ + lookupKey(Key).getOwner(Owner, *this); +} + +void +SDCSPSession::ChangeKeyOwner(const AccessCredentials &AccessCred, + const CssmKey &Key, + const CSSM_ACL_OWNER_PROTOTYPE &NewOwner) +{ + lookupKey(Key).changeOwner(AccessCred, + AclOwnerPrototype::overlay(NewOwner)); +} + +void +SDCSPSession::GetKeyAcl(const CssmKey &Key, + const CSSM_STRING *SelectionTag, + uint32 &NumberOfAclInfos, + CSSM_ACL_ENTRY_INFO_PTR &AclInfos) +{ + lookupKey(Key).getAcl(reinterpret_cast(SelectionTag), + NumberOfAclInfos, + reinterpret_cast(AclInfos), *this); +} + +void +SDCSPSession::ChangeKeyAcl(const AccessCredentials &AccessCred, + const CSSM_ACL_EDIT &AclEdit, + const CssmKey &Key) +{ + lookupKey(Key).changeAcl(AccessCred, AclEdit::overlay(AclEdit)); +} + +void +SDCSPSession::GetLoginOwner(CSSM_ACL_OWNER_PROTOTYPE &Owner) +{ + unimplemented(); +} + +void +SDCSPSession::ChangeLoginOwner(const AccessCredentials &AccessCred, + const CSSM_ACL_OWNER_PROTOTYPE &NewOwner) +{ + unimplemented(); +} + +void +SDCSPSession::GetLoginAcl(const CSSM_STRING *SelectionTag, + uint32 &NumberOfAclInfos, + CSSM_ACL_ENTRY_INFO_PTR &AclInfos) +{ + unimplemented(); +} + +void +SDCSPSession::ChangeLoginAcl(const AccessCredentials &AccessCred, + const CSSM_ACL_EDIT &AclEdit) +{ + unimplemented(); +} + + + +// +// Passthroughs +// +void +SDCSPSession::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(outData)); + + // we'll take the key from the context + const CssmKey &key = + context.get(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(CSSM_ATTRIBUTE_KEY, CSSMERR_CSP_MISSING_ATTR_KEY); + KeyHandle keyHandle = lookupKey(key).keyHandle(); + + // allocate digest holder on app's behalf + CSSM_DATA *digest = alloc(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 SDCSPSession::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 */ +}