X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/a66d0d4aa363c2f8ee16aee8810732ae89bc0608..9a27adb241486ab7597ffaea2b5613cd3d8e1f60:/Keychain/KeyItem.cpp?ds=sidebyside diff --git a/Keychain/KeyItem.cpp b/Keychain/KeyItem.cpp index 3147b06e..5e41c74f 100644 --- a/Keychain/KeyItem.cpp +++ b/Keychain/KeyItem.cpp @@ -25,52 +25,146 @@ #include #include #include +#include #include +#include "clNssUtils.h" +#include "KCEventNotifier.h" // @@@ This needs to be shared. -static CSSM_DB_NAME_ATTR(kSecKeyLabel, 6, "Label", 0, NULL, BLOB); static CSSM_DB_NAME_ATTR(kSecKeyPrintName, 1, "PrintName", 0, NULL, BLOB); +static CSSM_DB_NAME_ATTR(kSecKeyLabel, 6, "Label", 0, NULL, BLOB); +static CSSM_DB_NAME_ATTR(kSecApplicationTag, 7, "ApplicationTag", 0, NULL, BLOB); using namespace KeychainCore; KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) : ItemImpl(keychain, primaryKey, uniqueId), - mKey(NULL) + mKey() { } KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey) : ItemImpl(keychain, primaryKey), - mKey(NULL) + mKey() { } KeyItem::KeyItem(KeyItem &keyItem) : ItemImpl(keyItem), - mKey(NULL) + mKey() { + // @@@ this doesn't work for keys that are not in a keychain. } -KeyItem::~KeyItem() +KeyItem::KeyItem(const CssmClient::Key &key) : + ItemImpl(key->keyClass() + CSSM_DL_DB_RECORD_PUBLIC_KEY, (OSType)0, (UInt32)0, (const void*)NULL), + mKey(key) +{ + if (key->keyClass() > CSSM_KEYCLASS_SESSION_KEY) + MacOSError::throwMe(paramErr); +} + +KeyItem::~KeyItem() throw() { - if (mKey) - { - CssmClient::SSDbUniqueRecord uniqueId(ssDbUniqueRecord()); - uniqueId->database()->csp()->freeKey(*mKey); - uniqueId->allocator().free(mKey); - } } void KeyItem::update() { - MacOSError::throwMe(unimpErr); + ItemImpl::update(); } Item -KeyItem::copyTo(const Keychain &keychain) +KeyItem::copyTo(const Keychain &keychain, Access *newAccess) { - MacOSError::throwMe(unimpErr); + if (!keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP) + MacOSError::throwMe(errSecInvalidKeychain); + + /* Get the destination keychains db. */ + SSDb ssDb(safe_cast(&(*keychain->database()))); + + /* Make sure mKey is valid. */ + key(); + + // Generate a random label to use initially + CssmClient::CSP appleCsp(gGuidAppleCSP); + CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW); + uint8 labelBytes[20]; + CssmData label(labelBytes, sizeof(labelBytes)); + random.generate(label, label.Length); + + /* Set up the ACL for the new key. */ + SecPointer access; + if (newAccess) + access = newAccess; + else + access = new Access(*mKey); + + /* Generate a random 3DES wrapping Key. */ + CssmClient::GenerateKey genKey(csp(), CSSM_ALGID_3DES_3KEY, 192); + CssmClient::Key wrappingKey(genKey(KeySpec(CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP, + CSSM_KEYATTR_EXTRACTABLE /* | CSSM_KEYATTR_RETURN_DATA */))); + + /* Extract the key by wrapping it with the wrapping key. */ + CssmClient::WrapKey wrap(csp(), CSSM_ALGID_3DES_3KEY_EDE); + wrap.key(wrappingKey); + wrap.cred(getCredentials(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, kSecCredentialTypeDefault)); + wrap.mode(CSSM_ALGMODE_ECBPad); + wrap.padding(CSSM_PADDING_PKCS7); + CssmClient::Key wrappedKey(wrap(mKey)); + + /* Unwrap the new key into the new Keychain. */ + CssmClient::UnwrapKey unwrap(keychain->csp(), CSSM_ALGID_3DES_3KEY_EDE); + unwrap.key(wrappingKey); + unwrap.mode(CSSM_ALGMODE_ECBPad); + unwrap.padding(CSSM_PADDING_PKCS7); + + /* Setup the dldbHandle in the context. */ + unwrap.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, ssDb->handle()); + + /* Set up an initial aclEntry so we can change it after the unwrap. */ + Access::Maker maker; + ResourceControlContext rcc; + maker.initialOwner(rcc, NULL); + unwrap.aclEntry(rcc.input()); + + /* Unwrap the key. */ + uint32 usage = mKey->usage(); + /* Work around csp brokeness where it sets all usage bits in the Keyheader when CSSM_KEYUSE_ANY is set. */ + if (usage & CSSM_KEYUSE_ANY) + usage = CSSM_KEYUSE_ANY; + + CssmClient::Key unwrappedKey(unwrap(wrappedKey, KeySpec(usage, + (mKey->attributes() | CSSM_KEYATTR_PERMANENT) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE), + label))); + + /* Look up unwrapped key in the DLDB. */ + DbUniqueRecord uniqueId; + SSDbCursor dbCursor(ssDb, 1); + dbCursor->recordType(recordType()); + dbCursor->add(CSSM_DB_EQUAL, kSecKeyLabel, label); + CssmClient::Key copiedKey; + if (!dbCursor->nextKey(NULL, copiedKey, uniqueId)) + MacOSError::throwMe(errSecItemNotFound); + + /* Copy the Label, PrintName and ApplicationTag attributes from the old key to the new one. */ + dbUniqueRecord(); + DbAttributes oldDbAttributes(mUniqueId->database(), 3); + oldDbAttributes.add(kSecKeyLabel); + oldDbAttributes.add(kSecKeyPrintName); + oldDbAttributes.add(kSecApplicationTag); + mUniqueId->get(&oldDbAttributes, NULL); + uniqueId->modify(recordType(), &oldDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); + + /* Set the acl and owner on the unwrapped key. */ + access->setAccess(*unwrappedKey, maker); + + /* Return a keychain items which represents the new key. */ + Item item(keychain->item(recordType(), uniqueId)); + + KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, item); + + return item; } void @@ -91,20 +185,66 @@ KeyItem::ssDbUniqueRecord() return CssmClient::SSDbUniqueRecord(safe_cast(impl)); } -const CssmKey & -KeyItem::cssmKey() +CssmClient::Key & +KeyItem::key() { if (!mKey) { CssmClient::SSDbUniqueRecord uniqueId(ssDbUniqueRecord()); CssmDataContainer dataBlob(uniqueId->allocator()); uniqueId->get(NULL, &dataBlob); - mKey = reinterpret_cast(dataBlob.Data); - dataBlob.Data = NULL; - dataBlob.Length = 0; + mKey = CssmClient::Key(uniqueId->database()->csp(), *reinterpret_cast(dataBlob.Data)); } - return *mKey; + return mKey; +} + +CssmClient::CSP +KeyItem::csp() +{ + return key()->csp(); +} + + +const CSSM_X509_ALGORITHM_IDENTIFIER& +KeyItem::algorithmIdentifier() +{ +#if 0 + CssmKey *mKey; + CSSM_KEY_TYPE algorithm + CSSM_KEY_PTR cssmKey = (CSSM_KEY_PTR)thisData->Data; +cssmKey->KeyHeader + static void printKeyHeader( + const CSSM_KEYHEADER &hdr) +{ + printf(" Algorithm : "); + switch(hdr.AlgorithmId) { +CSSM_X509_ALGORITHM_IDENTIFIER algID; + +CSSM_OID *CL_algToOid( + CSSM_ALGORITHMS algId) +typedef struct cssm_x509_algorithm_identifier { + CSSM_OID algorithm; + CSSM_DATA parameters; +} CSSM_X509_ALGORITHM_IDENTIFIER, *CSSM_X509_ALGORITHM_IDENTIFIER_PTR; +#endif + + abort(); +} + +unsigned int +KeyItem::strengthInBits(const CSSM_X509_ALGORITHM_IDENTIFIER *algid) +{ + // @@@ Make a context with key based on algid and use that to get the effective keysize and not just the logical one. + CSSM_KEY_SIZE keySize = {}; + CSSM_RETURN rv = CSSM_QueryKeySizeInBits (csp()->handle(), + NULL, + key(), + &keySize); + if (rv) + return 0; + + return keySize.LogicalKeySizeInBits; } const AccessCredentials * @@ -113,6 +253,9 @@ KeyItem::getCredentials( SecCredentialType credentialType) { // @@@ Fix this to actually examine the ACL for this key and consider operation and do the right thing. + //AutoAclEntryInfoList aclInfos; + //key()->getAcl(aclInfos); + AclFactory factory; switch (credentialType) { @@ -137,9 +280,9 @@ KeyItem::createPair( uint32 publicKeyAttr, CSSM_KEYUSE privateKeyUsage, uint32 privateKeyAttr, - RefPointer initialAccess, - RefPointer &outPublicKey, - RefPointer &outPrivateKey) + SecPointer initialAccess, + SecPointer &outPublicKey, + SecPointer &outPrivateKey) { bool freeKeys = false; bool deleteContext = false; @@ -301,9 +444,9 @@ KeyItem::importPair( Keychain keychain, const CSSM_KEY &publicWrappedKey, const CSSM_KEY &privateWrappedKey, - RefPointer initialAccess, - RefPointer &outPublicKey, - RefPointer &outPrivateKey) + SecPointer initialAccess, + SecPointer &outPublicKey, + SecPointer &outPrivateKey) { bool freePublicKey = false; bool freePrivateKey = false; @@ -473,3 +616,144 @@ KeyItem::importPair( if (deleteContext) CSSM_DeleteContext(ccHandle); } + +KeyItem * +KeyItem::generate(Keychain keychain, + CSSM_ALGORITHMS algorithm, + uint32 keySizeInBits, + CSSM_CC_HANDLE contextHandle, + CSSM_KEYUSE keyUsage, + uint32 keyAttr, + SecPointer initialAccess) +{ + CssmClient::CSP appleCsp(gGuidAppleCSP); + CssmClient::CSP csp(NULL); + SSDb ssDb(NULL); + uint8 labelBytes[20]; + CssmData label(labelBytes, sizeof(labelBytes)); + bool freeKey = false; + bool deleteContext = false; + const CSSM_DATA *plabel = NULL; + KeyItem *outKey; + + if (keychain) + { + if (!keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP) + MacOSError::throwMe(errSecInvalidKeychain); + + ssDb = SSDb(safe_cast(&(*keychain->database()))); + csp = keychain->csp(); + + // Generate a random label to use initially + CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW); + random.generate(label, label.Length); + plabel = &label; + } + else + { + // Not a persistant key so create it in the regular csp + csp = appleCsp; + } + + // Create a Access::Maker for the initial owner of the private key. + ResourceControlContext *prcc = NULL, rcc; + const AccessCredentials *cred = NULL; + Access::Maker maker; + if (keychain && initialAccess) + { + memset(&rcc, 0, sizeof(rcc)); + // @@@ Potentially provide a credential argument which allows us to generate keys in the csp. Currently the CSP let's anyone do this, but we might restrict this in the future, f.e. a smartcard could require out of band pin entry before a key can be generated. + maker.initialOwner(rcc); + // Create the cred we need to manipulate the keys until we actually set a new access control for them. + cred = maker.cred(); + prcc = &rcc; + } + + CSSM_KEY cssmKey; + + CSSM_CC_HANDLE ccHandle = 0; + + try + { + CSSM_RETURN status; + if (contextHandle) + ccHandle = contextHandle; + else + { + status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle); + if (status) + CssmError::throwMe(status); + deleteContext = true; + } + + if (ssDb) + { + CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle(); + CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle; + CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } }; + status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes); + if (status) + CssmError::throwMe(status); + + keyAttr |= CSSM_KEYATTR_PERMANENT; + } + + // Generate the key + status = CSSM_GenerateKey(ccHandle, keyUsage, keyAttr, plabel, prcc, &cssmKey); + if (status) + CssmError::throwMe(status); + + if (ssDb) + { + freeKey = true; + // Find the keys we just generated in the DL to get SecKeyRef's to them + // so we can change the label to be the hash of the public key, and + // fix up other attributes. + + // Look up key in the DLDB. + DbAttributes dbAttributes; + DbUniqueRecord uniqueId; + SSDbCursor dbCursor(ssDb, 1); + dbCursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY); + dbCursor->add(CSSM_DB_EQUAL, kSecKeyLabel, label); + CssmClient::Key key; + if (!dbCursor->nextKey(&dbAttributes, key, uniqueId)) + MacOSError::throwMe(errSecItemNotFound); + + // Finally fix the acl and owner of the key to the specified access control settings. + if (initialAccess) + initialAccess->setAccess(*key, maker); + + // Create keychain items which will represent the keys. + outKey = safe_cast(&(*keychain->item(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, uniqueId))); + } + else + { + CssmClient::Key tempKey(csp, cssmKey); + outKey = new KeyItem(tempKey); + } + } + catch (...) + { + if (freeKey) + { + // Delete the keys if something goes wrong so we don't end up with inaccesable keys in the database. + CSSM_FreeKey(csp->handle(), cred, &cssmKey, TRUE); + } + + if (deleteContext) + CSSM_DeleteContext(ccHandle); + + throw; + } + + if (freeKey) + { + CSSM_FreeKey(csp->handle(), NULL, &cssmKey, FALSE); + } + + if (deleteContext) + CSSM_DeleteContext(ccHandle); + + return outKey; +}