/* * 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. */ // // SSKey - reference keys for the security server // #include "SSKey.h" #include "SSCSPSession.h" #include "SSCSPDLSession.h" #include "SSDatabase.h" #include "SSDLSession.h" #include "KeySchema.h" #include using namespace CssmClient; using namespace SecurityServer; // Constructor for a Security Server generated key. SSKey::SSKey(SSCSPSession &session, KeyHandle keyHandle, CssmKey &ioKey, SSDatabase &inSSDatabase, uint32 inKeyAttr, const CssmData *inKeyLabel) : ReferencedKey(session.mSSCSPDLSession), mAllocator(session), mKeyHandle(keyHandle), mClientSession(session.clientSession()) { CssmKey::Header &header = ioKey.header(); if (inKeyAttr & CSSM_KEYATTR_PERMANENT) { if (!inSSDatabase) CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_DL_DB_HANDLE); // EncodeKey and store it in the db. CssmDataContainer blob(mAllocator); clientSession().encodeKey(keyHandle, blob); assert(header.HeaderVersion == CSSM_KEYHEADER_VERSION); switch (header.KeyClass) { case CSSM_KEYCLASS_PUBLIC_KEY: mRecordType = CSSM_DL_DB_RECORD_PUBLIC_KEY; break; case CSSM_KEYCLASS_PRIVATE_KEY: mRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; break; case CSSM_KEYCLASS_SESSION_KEY: mRecordType = CSSM_DL_DB_RECORD_SYMMETRIC_KEY; break; default: CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); } CssmData label; if (inKeyLabel) label = *inKeyLabel; CssmData none; // We store the keys real CSP guid on disk CssmGuidData creatorGuid(header.CspId); CssmDateData startDate(header.StartDate); CssmDateData endDate(header.EndDate); DbAttributes attributes(inSSDatabase); attributes.recordType(mRecordType); attributes.add(KeySchema::KeyClass, mRecordType); attributes.add(KeySchema::PrintName, label); attributes.add(KeySchema::Alias, none); attributes.add(KeySchema::Permanent, header.attribute(CSSM_KEYATTR_PERMANENT)); attributes.add(KeySchema::Private, header.attribute(CSSM_KEYATTR_PRIVATE)); attributes.add(KeySchema::Modifiable, header.attribute(CSSM_KEYATTR_MODIFIABLE)); attributes.add(KeySchema::Label, label); attributes.add(KeySchema::ApplicationTag, none); attributes.add(KeySchema::KeyCreator, creatorGuid); attributes.add(KeySchema::KeyType, header.AlgorithmId); attributes.add(KeySchema::KeySizeInBits, header.LogicalKeySizeInBits); // @@@ Get the real effective key size. attributes.add(KeySchema::EffectiveKeySize, header.LogicalKeySizeInBits); attributes.add(KeySchema::StartDate, startDate); attributes.add(KeySchema::EndDate, endDate); attributes.add(KeySchema::Sensitive, header.attribute(CSSM_KEYATTR_SENSITIVE)); attributes.add(KeySchema::AlwaysSensitive, header.attribute(CSSM_KEYATTR_ALWAYS_SENSITIVE)); attributes.add(KeySchema::Extractable, header.attribute(CSSM_KEYATTR_EXTRACTABLE)); attributes.add(KeySchema::NeverExtractable, header.attribute(CSSM_KEYATTR_NEVER_EXTRACTABLE)); attributes.add(KeySchema::Encrypt, header.useFor(CSSM_KEYUSE_ANY | CSSM_KEYUSE_ENCRYPT)); attributes.add(KeySchema::Decrypt, header.useFor(CSSM_KEYUSE_ANY | CSSM_KEYUSE_DECRYPT)); attributes.add(KeySchema::Derive, header.useFor(CSSM_KEYUSE_ANY | CSSM_KEYUSE_DERIVE)); attributes.add(KeySchema::Sign, header.useFor(CSSM_KEYUSE_ANY | CSSM_KEYUSE_SIGN)); attributes.add(KeySchema::Verify, header.useFor(CSSM_KEYUSE_ANY | CSSM_KEYUSE_VERIFY)); attributes.add(KeySchema::SignRecover, header.useFor(CSSM_KEYUSE_ANY | CSSM_KEYUSE_SIGN_RECOVER)); attributes.add(KeySchema::VerifyRecover, header.useFor(CSSM_KEYUSE_ANY | CSSM_KEYUSE_VERIFY_RECOVER)); attributes.add(KeySchema::Wrap, header.useFor(CSSM_KEYUSE_ANY | CSSM_KEYUSE_WRAP)); attributes.add(KeySchema::Unwrap, header.useFor(CSSM_KEYUSE_ANY | CSSM_KEYUSE_UNWRAP)); // @@@ Fixme mUniqueId = inSSDatabase->insert(mRecordType, &attributes, &blob, true); } header.cspGuid(session.plugin.myGuid()); // Set the csp guid to me. makeReferenceKey(mAllocator, keyReference(), ioKey); } // Constructor for a key retrived from a Db. SSKey::SSKey(SSDLSession &session, CssmKey &ioKey, SSDatabase &inSSDatabase, const SSUniqueRecord &uniqueId, CSSM_DB_RECORDTYPE recordType, CssmData &keyBlob) : ReferencedKey(session.mSSCSPDLSession), mAllocator(session.allocator()), mKeyHandle(noKey), mUniqueId(uniqueId), mRecordType(recordType), mClientSession(session.clientSession()) { CssmKey::Header &header = ioKey.header(); memset(&header, 0, sizeof(header)); // Clear key header if (!mUniqueId || !mUniqueId->database()) CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); header.HeaderVersion = CSSM_KEYHEADER_VERSION; switch (mRecordType) { case CSSM_DL_DB_RECORD_PUBLIC_KEY: header.KeyClass = CSSM_KEYCLASS_PUBLIC_KEY; break; case CSSM_DL_DB_RECORD_PRIVATE_KEY: header.KeyClass = CSSM_KEYCLASS_PRIVATE_KEY; break; case CSSM_DL_DB_RECORD_SYMMETRIC_KEY: header.KeyClass = CSSM_KEYCLASS_SESSION_KEY; break; default: CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); } DbAttributes attributes(mUniqueId->database()); attributes.recordType(mRecordType); attributes.add(KeySchema::KeyClass); // 0 attributes.add(KeySchema::Permanent); // 1 attributes.add(KeySchema::Private); // 2 attributes.add(KeySchema::Modifiable); // 3 attributes.add(KeySchema::KeyCreator); // 4 attributes.add(KeySchema::KeyType); // 5 attributes.add(KeySchema::KeySizeInBits); // 6 attributes.add(KeySchema::StartDate); // 7 attributes.add(KeySchema::EndDate); // 8 attributes.add(KeySchema::Sensitive); // 9 attributes.add(KeySchema::AlwaysSensitive); // 10 attributes.add(KeySchema::Extractable); // 11 attributes.add(KeySchema::NeverExtractable); // 12 attributes.add(KeySchema::Encrypt); // 13 attributes.add(KeySchema::Decrypt); // 14 attributes.add(KeySchema::Derive); // 15 attributes.add(KeySchema::Sign); // 16 attributes.add(KeySchema::Verify); // 17 attributes.add(KeySchema::SignRecover); // 18 attributes.add(KeySchema::VerifyRecover); // 19 attributes.add(KeySchema::Wrap); // 20 attributes.add(KeySchema::Unwrap); // 21 mUniqueId->get(&attributes, NULL); // Assert that the mRecordType matches the KeyClass attribute. if (mRecordType != uint32(attributes[0])) CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); header.AlgorithmId = attributes[5]; // KeyType header.LogicalKeySizeInBits = attributes[6]; // KeySizeInBits if (attributes[1]) header.setAttribute(CSSM_KEYATTR_PERMANENT); if (attributes[2]) header.setAttribute(CSSM_KEYATTR_PRIVATE); if (attributes[3]) header.setAttribute(CSSM_KEYATTR_MODIFIABLE); if (attributes[9]) header.setAttribute(CSSM_KEYATTR_SENSITIVE); if (attributes[11]) header.setAttribute(CSSM_KEYATTR_EXTRACTABLE); if (attributes[10]) header.setAttribute(CSSM_KEYATTR_ALWAYS_SENSITIVE); if (attributes[12]) header.setAttribute(CSSM_KEYATTR_NEVER_EXTRACTABLE); if (attributes[13]) header.usage(CSSM_KEYUSE_ENCRYPT); if (attributes[14]) header.usage(CSSM_KEYUSE_DECRYPT); if (attributes[15]) header.usage(CSSM_KEYUSE_DERIVE); if (attributes[16]) header.usage(CSSM_KEYUSE_SIGN); if (attributes[17]) header.usage(CSSM_KEYUSE_VERIFY); if (attributes[18]) header.usage(CSSM_KEYUSE_SIGN_RECOVER); if (attributes[19]) header.usage(CSSM_KEYUSE_VERIFY_RECOVER); if (attributes[20]) header.usage(CSSM_KEYUSE_WRAP); if (attributes[21]) header.usage(CSSM_KEYUSE_UNWRAP); // If all usages are allowed set usage to CSSM_KEYUSE_ANY if (header.usage() == (CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_DERIVE | CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_SIGN_RECOVER | CSSM_KEYUSE_VERIFY_RECOVER | CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP)) header.usage(CSSM_KEYUSE_ANY); if (!attributes[7].size() || !attributes[8].size()) CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); header.StartDate = attributes[7].at(0); header.EndDate = attributes[8].at(0); makeReferenceKey(mAllocator, keyReference(), ioKey); header.cspGuid(session.plugin.myGuid()); // Set the csp guid to me. } SSKey::~SSKey() { if (mKeyHandle != noKey) clientSession().releaseKey(mKeyHandle); } void SSKey::free(const AccessCredentials *accessCred, CssmKey &ioKey, CSSM_BOOL deleteKey) { freeReferenceKey(mAllocator, ioKey); if (deleteKey) { if (!mUniqueId || !mUniqueId->database()) CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); // @@@ Evaluate accessCred against Db acl. // What should we do with accessCred? Reauthenticate // mUniqueId->database()? mUniqueId->deleteRecord(); } if (mKeyHandle != noKey) { clientSession().releaseKey(mKeyHandle); mKeyHandle = noKey; } } SecurityServer::ClientSession & SSKey::clientSession() { return mClientSession; } KeyHandle SSKey::optionalKeyHandle() const { return mKeyHandle; } KeyHandle SSKey::keyHandle() { if (mKeyHandle == noKey) { // Deal with uninstantiated keys. if (!mUniqueId || !mUniqueId->database()) CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); CssmDataContainer blob(mAllocator); mUniqueId->get(NULL, &blob); CssmKey::Header dummyHeader; // @@@ Unused mKeyHandle = clientSession().decodeKey(mUniqueId->database().dbHandle(), blob, dummyHeader); // @@@ Check decoded header against returned header } return mKeyHandle; } // // ACL retrieval and change operations // void SSKey::getOwner(CSSM_ACL_OWNER_PROTOTYPE &owner, CssmAllocator &allocator) { clientSession().getKeyOwner(keyHandle(), AclOwnerPrototype::overlay(owner), allocator); } void SSKey::changeOwner(const AccessCredentials &accessCred, const AclOwnerPrototype &newOwner) { clientSession().changeKeyOwner(keyHandle(), accessCred, newOwner); didChangeAcl(); } void SSKey::getAcl(const char *selectionTag, uint32 &numberOfAclInfos, AclEntryInfo *&aclInfos, CssmAllocator &allocator) { clientSession().getKeyAcl(keyHandle(), selectionTag, numberOfAclInfos, aclInfos, allocator); } void SSKey::changeAcl(const AccessCredentials &accessCred, const AclEdit &aclEdit) { clientSession().changeKeyAcl(keyHandle(), accessCred, aclEdit); didChangeAcl(); } void SSKey::didChangeAcl() { if (mUniqueId == true) { secdebug("keyacl", "SSKey::didChangeAcl() keyHandle: %lu updating DL entry", mKeyHandle); // The key is persistant, make the change on disk. CssmDataContainer keyBlob(mAllocator); clientSession().encodeKey(keyHandle(), keyBlob); mUniqueId->modify(mRecordType, NULL, &keyBlob, CSSM_DB_MODIFY_ATTRIBUTE_NONE); } else { secdebug("keyacl", "SSKey::didChangeAcl() keyHandle: %lu transient key no update done", mKeyHandle); } }