/* * 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 #include 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_ATTRIBUTE_DL_DB_HANDLE)); } SSDatabase SSCSPSession::getDatabase(CSSM_DL_DB_HANDLE *aDLDbHandle) { if (aDLDbHandle) return findSession(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(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(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(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(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(&mSSCSPDLSession.find(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(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(SelectionTag), NumberOfAclInfos, reinterpret_cast(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(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 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 */ }