+++ /dev/null
-/*
- * Copyright (c) 2000-2001,2011-2014 Apple 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.
- */
-
-
-#include "securestorage.h"
-#include <security_cdsa_client/genkey.h>
-//#include <Security/Access.h> //@@@CONV
-#include <security_utilities/osxcode.h>
-#include <memory>
-
-using namespace CssmClient;
-//using namespace KeychainCore;
-
-//
-// Manage CSPDL attachments
-//
-CSPDLImpl::CSPDLImpl(const Guid &guid)
-: CSPImpl(Cssm::standard()->autoModule(guid)),
-DLImpl(CSPImpl::module())
-{
-}
-
-CSPDLImpl::CSPDLImpl(const Module &module)
-: CSPImpl(module),
-DLImpl(module)
-{
-}
-
-CSPDLImpl::~CSPDLImpl()
-try
-{
-}
-catch (...)
-{
-}
-
-Allocator &CSPDLImpl::allocator() const
-{
- DLImpl::allocator(); return CSPImpl::allocator();
-}
-
-void CSPDLImpl::allocator(Allocator &alloc)
-{
- CSPImpl::allocator(alloc); DLImpl::allocator(alloc);
-}
-
-bool CSPDLImpl::operator <(const CSPDLImpl &other) const
-{
- return (static_cast<const CSPImpl &>(*this) < static_cast<const CSPImpl &>(other) ||
- (!(static_cast<const CSPImpl &>(other) < static_cast<const CSPImpl &>(*this))
- && static_cast<const DLImpl &>(*this) < static_cast<const DLImpl &>(other)));
-}
-
-bool CSPDLImpl::operator ==(const CSPDLImpl &other) const
-{
- return (static_cast<const CSPImpl &>(*this) == static_cast<const CSPImpl &>(other)
- && static_cast<const DLImpl &>(*this) == static_cast<const DLImpl &>(other));
-}
-
-CSSM_SERVICE_MASK CSPDLImpl::subserviceMask() const
-{
- return CSPImpl::subserviceType() | DLImpl::subserviceType();
-}
-
-void CSPDLImpl::subserviceId(uint32 id)
-{
- CSPImpl::subserviceId(id); DLImpl::subserviceId(id);
-}
-
-
-//
-// Secure storage
-//
-SSCSPDLImpl::SSCSPDLImpl(const Guid &guid) : CSPDLImpl::CSPDLImpl(guid)
-{
-}
-
-SSCSPDLImpl::SSCSPDLImpl(const Module &module) : CSPDLImpl::CSPDLImpl(module)
-{
-}
-
-SSCSPDLImpl::~SSCSPDLImpl()
-{
-}
-
-DbImpl *
-SSCSPDLImpl::newDb(const char *inDbName, const CSSM_NET_ADDRESS *inDbLocation)
-{
- return new SSDbImpl(SSCSPDL(this), inDbName, inDbLocation);
-}
-
-
-//
-// SSDbImpl -- Secure Storage Database Implementation
-//
-SSDbImpl::SSDbImpl(const SSCSPDL &cspdl, const char *inDbName,
- const CSSM_NET_ADDRESS *inDbLocation)
-: DbImpl(cspdl, inDbName, inDbLocation)
-{
-}
-
-SSDbImpl::~SSDbImpl()
-{
-}
-
-void
-SSDbImpl::create()
-{
- DbImpl::create();
-}
-
-void
-SSDbImpl::open()
-{
- DbImpl::open();
-}
-
-SSDbUniqueRecord
-SSDbImpl::insert(CSSM_DB_RECORDTYPE recordType,
- const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes,
- const CSSM_DATA *data,
- const CSSM_RESOURCE_CONTROL_CONTEXT *rc)
-{
- // Get the handle of the DL underlying this CSPDL.
- CSSM_DL_DB_HANDLE dldbh;
- passThrough(CSSM_APPLECSPDL_DB_GET_HANDLE, NULL,
- reinterpret_cast<void **>(&dldbh));
-
- // Turn off autocommit on the underlying DL and remember the old state.
- CSSM_BOOL autoCommit = CSSM_TRUE;
- check(CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
- 0, reinterpret_cast<void **>(&autoCommit)));
- SSGroup group(SSDb(this), rc);
- const CSSM_ACCESS_CREDENTIALS *cred = rc ? rc->AccessCred : NULL;
- try
- {
- return insert(recordType, attributes, data, group, cred);
- if (autoCommit)
- {
- // autoCommit was on so commit now that we are done and turn
- // it back on.
- check(CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_COMMIT, NULL, NULL));
- CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
- reinterpret_cast<const void *>(autoCommit), NULL);
- }
- }
- catch(...)
- {
- try { group->deleteKey(cred); } catch (...) {}
- if (autoCommit)
- {
- // autoCommit was off so rollback since we failed and turn
- // autoCommit back on.
- CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_ROLLBACK, NULL, NULL);
- CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
- reinterpret_cast<const void *>(autoCommit), NULL);
- }
- throw;
- }
-
- // keep the compiler happy -- this path is NEVER taken
- CssmError::throwMe(0);
-}
-
-SSDbUniqueRecord
-SSDbImpl::insert(CSSM_DB_RECORDTYPE recordType,
- const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes,
- const CSSM_DATA *data, const SSGroup &group,
- const CSSM_ACCESS_CREDENTIALS *cred)
-{
- // Create an encoded dataBlob for this item.
- CssmDataContainer dataBlob(allocator());
- group->encodeDataBlob(data, cred, dataBlob);
-
- // Insert the record with the new juicy dataBlob.
- return SSDbUniqueRecord(safe_cast<SSDbUniqueRecordImpl *>
- (&(*DbImpl::insert(recordType, attributes, &dataBlob))));
-}
-
-
-// DbCursorMaker
-DbCursorImpl *
-SSDbImpl::newDbCursor(const CSSM_QUERY &query, Allocator &allocator)
-{
- return new SSDbCursorImpl(Db(this), query, allocator);
-}
-
-DbCursorImpl *
-SSDbImpl::newDbCursor(uint32 capacity, Allocator &allocator)
-{
- return new SSDbCursorImpl(Db(this), capacity, allocator);
-}
-
-
-// SSDbUniqueRecordMaker
-DbUniqueRecordImpl *
-SSDbImpl::newDbUniqueRecord()
-{
- return new SSDbUniqueRecordImpl(Db(this));
-}
-
-
-//
-// SSGroup -- Group key with acl, used to protect a group of items.
-//
-// @@@ Get this from a shared spot.
-CSSM_DB_NAME_ATTR(SSGroupImpl::kLabel, 6, (char*) "Label", 0, NULL, BLOB);
-
-// Create a new group.
-SSGroupImpl::SSGroupImpl(const SSDb &ssDb,
- const CSSM_RESOURCE_CONTROL_CONTEXT *credAndAclEntry)
-: KeyImpl(ssDb->csp()), mLabel(ssDb->allocator())
-{
- mLabel.Length = kLabelSize;
- mLabel.Data = reinterpret_cast<uint8 *>
- (mLabel.mAllocator.malloc(mLabel.Length));
-
- // Get our csp and set up a random number generation context.
- CSP csp(this->csp());
- Random random(csp, CSSM_ALGID_APPLE_YARROW);
-
- // Generate a kLabelSize byte random number that will be the label of
- // the key which we store in the dataBlob.
- random.generate(mLabel, (uint32)mLabel.Length);
-
- // Overwrite the first 4 bytes with the magic cookie for a group.
- reinterpret_cast<uint32 *>(mLabel.Data)[0] = h2n(uint32(kGroupMagic));
-
- // @@@ Ensure that the label is unique (Chance of collision is 2^80 --
- // birthday paradox).
-
- // Generate a permanent 3DES key that we will use to encrypt the data.
- GenerateKey genKey(csp, CSSM_ALGID_3DES_3KEY, 192);
- genKey.database(ssDb);
-
- // Set the acl of the key correctly here
- genKey.rcc(credAndAclEntry);
-
- // Generate the key
- genKey(*this, KeySpec(CSSM_KEYUSE_ENCRYPT|CSSM_KEYUSE_DECRYPT,
- CSSM_KEYATTR_PERMANENT|CSSM_KEYATTR_SENSITIVE,
- mLabel));
-
- // Activate ourself so CSSM_FreeKey will get called when we go out of
- // scope.
- activate();
-}
-
-// Lookup an existing group based on a dataBlob.
-SSGroupImpl::SSGroupImpl(const SSDb &ssDb, const CSSM_DATA &dataBlob)
-: KeyImpl(ssDb->csp()), mLabel(ssDb->allocator())
-{
- if (dataBlob.Length < kLabelSize + kIVSize)
- CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); // Not a SS record
-
- mLabel = CssmData(dataBlob.Data, kLabelSize);
- if (*reinterpret_cast<const uint32 *>(mLabel.Data) != h2n (uint32(kGroupMagic)))
- CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); // Not a SS record
-
- // Look up the symmetric key with that label.
- DbCursor cursor(new DbDbCursorImpl(ssDb, 0, Allocator::standard()));
- cursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
- cursor->add(CSSM_DB_EQUAL, kLabel, mLabel);
-
- DbUniqueRecord keyId;
- CssmDataContainer keyData(ssDb->allocator());
- if (!cursor->next(NULL, &keyData, keyId))
- CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND); // The key can't be found
-
- // Set the key part of ourself.
- static_cast<CSSM_KEY &>(*this) =
- *reinterpret_cast<const CSSM_KEY *>(keyData.Data);
-
- // Activate ourself so CSSM_FreeKey will get called when we go out of
- // scope.
- activate();
-}
-
-bool
-SSGroupImpl::isGroup(const CSSM_DATA &dataBlob)
-{
- return dataBlob.Length >= kLabelSize + kIVSize
- && *reinterpret_cast<const uint32 *>(dataBlob.Data) == h2n(uint32(kGroupMagic));
-}
-
-const CssmData
-SSGroupImpl::label() const
-{
- return mLabel;
-}
-
-void
-SSGroupImpl::decodeDataBlob(const CSSM_DATA &dataBlob,
- const CSSM_ACCESS_CREDENTIALS *cred,
- Allocator &allocator, CSSM_DATA &data)
-{
- // First get the IV and the cipherText from the blob.
- CssmData iv(&dataBlob.Data[kLabelSize], kIVSize);
- CssmData cipherText(&dataBlob.Data[kLabelSize + kIVSize],
- dataBlob.Length - (kLabelSize + kIVSize));
-
- CssmDataContainer plainText1(allocator);
- CssmDataContainer plainText2(allocator);
- // Decrypt the data
- // @@@ Don't use staged decrypt once the AppleCSPDL can do combo
- // encryption.
- // Setup decryption context
- Decrypt decrypt(csp(), algorithm());
- decrypt.mode(CSSM_ALGMODE_CBCPadIV8);
- decrypt.padding(CSSM_PADDING_PKCS1);
- decrypt.initVector(iv);
- decrypt.key(Key(this));
- decrypt.cred(AccessCredentials::overlay(cred));
- decrypt.decrypt(&cipherText, 1, &plainText1, 1);
- decrypt.final(plainText2);
-
- // Use DL allocator for allocating memory for data.
- CSSM_SIZE length = plainText1.Length + plainText2.Length;
- data.Data = allocator.alloc<uint8>((UInt32)length);
- data.Length = length;
- memcpy(data.Data, plainText1.Data, plainText1.Length);
- memcpy(&data.Data[plainText1.Length], plainText2.Data, plainText2.Length);
-}
-
-void
-SSGroupImpl::encodeDataBlob(const CSSM_DATA *data,
- const CSSM_ACCESS_CREDENTIALS *cred,
- CssmDataContainer &dataBlob)
-{
- // Get our csp and set up a random number generation context.
- CSP csp(this->csp());
- Random random(csp, CSSM_ALGID_APPLE_YARROW);
-
- // Encrypt data using key and encode it in a dataBlob.
-
- // First calculate a random IV.
- uint8 ivBuf[kIVSize];
- CssmData iv(ivBuf, kIVSize);
- random.generate(iv, kIVSize);
-
- // Setup encryption context
- Encrypt encrypt(csp, algorithm());
- encrypt.mode(CSSM_ALGMODE_CBCPadIV8);
- encrypt.padding(CSSM_PADDING_PKCS1);
- encrypt.initVector(iv);
- encrypt.key(Key(this));
- encrypt.cred(AccessCredentials::overlay(cred));
-
- // Encrypt the data
- const CssmData nothing;
- const CssmData *plainText = data ? CssmData::overlay(data) : ¬hing;
- // @@@ Don't use staged encrypt once the AppleCSPDL can do combo
- // encryption.
- CssmDataContainer cipherText1, cipherText2;
- encrypt.encrypt(plainText, 1, &cipherText1, 1);
- encrypt.final(cipherText2);
-
- // Create a dataBlob containing the label followed by the IV followed
- // by the cipherText.
- CSSM_SIZE length = (kLabelSize + kIVSize
- + cipherText1.Length + cipherText2.Length);
- dataBlob.Data = dataBlob.mAllocator.alloc<uint8>((UInt32)length);
- dataBlob.Length = length;
- memcpy(dataBlob.Data, mLabel.Data, kLabelSize);
- memcpy(&dataBlob.Data[kLabelSize], iv.Data, kIVSize);
- memcpy(&dataBlob.Data[kLabelSize + kIVSize],
- cipherText1.Data, cipherText1.Length);
- memcpy(&dataBlob.Data[kLabelSize + kIVSize + cipherText1.Length],
- cipherText2.Data, cipherText2.Length);
-}
-
-
-//
-// SSDbCursorImpl -- Secure Storage Database Cursor Implementation.
-//
-SSDbCursorImpl::SSDbCursorImpl(const Db &db, const CSSM_QUERY &query,
- Allocator &allocator)
-: DbDbCursorImpl(db, query, allocator)
-{
-}
-
-SSDbCursorImpl::SSDbCursorImpl(const Db &db, uint32 capacity,
- Allocator &allocator)
-: DbDbCursorImpl(db, capacity, allocator)
-{
-}
-
-bool
-SSDbCursorImpl::next(DbAttributes *attributes, ::CssmDataContainer *data,
- DbUniqueRecord &uniqueId)
-{
- return next(attributes, data, uniqueId, NULL);
-}
-
-bool
-SSDbCursorImpl::next(DbAttributes *attributes, ::CssmDataContainer *data,
- DbUniqueRecord &uniqueId,
- const CSSM_ACCESS_CREDENTIALS *cred)
-{
- if (!data)
- return DbDbCursorImpl::next(attributes, data, uniqueId);
-
- DbAttributes noAttrs, *attrs;
- attrs = attributes ? attributes : &noAttrs;
-
- // Get the datablob for this record
- CssmDataContainer dataBlob(allocator());
- for (;;)
- {
- if (!DbDbCursorImpl::next(attrs, &dataBlob, uniqueId))
- return false;
-
- // Keep going until we find a non key type record.
- CSSM_DB_RECORDTYPE rt = attrs->recordType();
- if (rt != CSSM_DL_DB_RECORD_SYMMETRIC_KEY
- && rt != CSSM_DL_DB_RECORD_PRIVATE_KEY
- && rt != CSSM_DL_DB_RECORD_PUBLIC_KEY)
- {
- // @@@ Check the label and if it doesn't start with the magic for a SSKey return the key.
- break;
- }
- else
- {
- // Free the key we just retrieved
- database()->csp()->freeKey(*reinterpret_cast<CssmKey *>(dataBlob.Data));
- }
- }
-
- if (!SSGroupImpl::isGroup(dataBlob))
- {
- data->Data = dataBlob.Data;
- data->Length = dataBlob.Length;
- dataBlob.Data = NULL;
- dataBlob.Length = 0;
- return true;
- }
-
- // Get the group for dataBlob
- SSGroup group(database(), dataBlob);
-
- // Decode the dataBlob, pass in the DL allocator.
- group->decodeDataBlob(dataBlob, cred, database()->allocator(), *data);
- return true;
-}
-
-bool
-SSDbCursorImpl::nextKey(DbAttributes *attributes, Key &key,
- DbUniqueRecord &uniqueId)
-{
- DbAttributes noAttrs, *attrs;
- attrs = attributes ? attributes : &noAttrs;
- CssmDataContainer keyData(database()->allocator());
- for (;;)
- {
- if (!DbDbCursorImpl::next(attrs, &keyData, uniqueId))
- return false;
- // Keep going until we find a key type record.
- CSSM_DB_RECORDTYPE rt = attrs->recordType();
- if (rt == CSSM_DL_DB_RECORD_SYMMETRIC_KEY
- || rt == CSSM_DL_DB_RECORD_PRIVATE_KEY
- || rt == CSSM_DL_DB_RECORD_PUBLIC_KEY)
- break;
- }
-
- key = Key(database()->csp(), *reinterpret_cast<CSSM_KEY *>(keyData.Data));
- return true;
-}
-
-void
-SSDbCursorImpl::activate()
-{
- return DbDbCursorImpl::activate();
-}
-
-void
-SSDbCursorImpl::deactivate()
-{
- return DbDbCursorImpl::deactivate();
-}
-
-
-//
-// SSDbUniqueRecordImpl -- Secure Storage UniqueRecord Implementation.
-//
-SSDbUniqueRecordImpl::SSDbUniqueRecordImpl(const Db &db)
-: DbUniqueRecordImpl(db)
-{
-}
-
-SSDbUniqueRecordImpl::~SSDbUniqueRecordImpl()
-{
-}
-
-void
-SSDbUniqueRecordImpl::deleteRecord()
-{
- deleteRecord(NULL);
-}
-
-void
-SSDbUniqueRecordImpl::deleteRecord(const CSSM_ACCESS_CREDENTIALS *cred)
-{
- // Get the datablob for this record
- // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
- CssmDataContainer dataBlob(allocator());
- DbAttributes attributes;
-
- DbUniqueRecordImpl::get(&attributes, &dataBlob);
- CSSM_KEY_PTR keyPtr = (CSSM_KEY_PTR) dataBlob.data();
-
- // delete data part first:
- // (1) don't leave data without keys around
- // (2) delete orphaned data anyway
- DbUniqueRecordImpl::deleteRecord();
-
- // @@@ Use transactions?
- if (SSGroupImpl::isGroup(dataBlob))
- try {
- // Get the group for dataBlob
- SSGroup group(database(), dataBlob);
- // Delete the group (key)
- group->deleteKey(cred);
- } catch (const CssmError &err) {
- switch (err.error) {
- case CSSMERR_DL_RECORD_NOT_FOUND:
- // Zombie item (no group key). Finally at peace! No error
- break;
- default:
-
- if (attributes.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY ||
- attributes.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY ||
- attributes.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
- {
- allocator().free(keyPtr->KeyData.Data);
- }
-
- throw;
- }
- }
-
- if (attributes.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY ||
- attributes.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY ||
- attributes.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
- {
- allocator().free(keyPtr->KeyData.Data);
- }
-}
-
-void
-SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType,
- const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes,
- const CSSM_DATA *data,
- CSSM_DB_MODIFY_MODE modifyMode)
-{
- modify(recordType, attributes, data, modifyMode, NULL);
-}
-
-void
-SSDbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType,
- const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes,
- const CSSM_DATA *data,
- CSSM_DB_MODIFY_MODE modifyMode,
- const CSSM_ACCESS_CREDENTIALS *cred)
-{
- if (!data)
- {
- DbUniqueRecordImpl::modify(recordType, attributes, NULL, modifyMode);
- return;
- }
-
- // Get the datablob for this record
- // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
- CssmDataContainer oldDataBlob(allocator());
- DbUniqueRecordImpl::get(NULL, &oldDataBlob);
-
- if (!SSGroupImpl::isGroup(oldDataBlob))
- {
- DbUniqueRecordImpl::modify(recordType, attributes, data, modifyMode);
- return;
- }
-
- // Get the group for oldDataBlob
- SSGroup group(database(), oldDataBlob);
-
- // Create a new dataBlob.
- CssmDataContainer dataBlob(allocator());
- group->encodeDataBlob(data, cred, dataBlob);
- DbUniqueRecordImpl::modify(recordType, attributes, &dataBlob, modifyMode);
-}
-
-void
-SSDbUniqueRecordImpl::get(DbAttributes *attributes, ::CssmDataContainer *data)
-{
- get(attributes, data, NULL);
-}
-
-void
-SSDbUniqueRecordImpl::get(DbAttributes *attributes, ::CssmDataContainer *data,
- const CSSM_ACCESS_CREDENTIALS *cred)
-{
- if (!data)
- {
- DbUniqueRecordImpl::get(attributes, NULL);
- return;
- }
-
- // Get the datablob for this record
- // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
- CssmDataContainer dataBlob(allocator());
- DbUniqueRecordImpl::get(attributes, &dataBlob);
-
- if (!SSGroupImpl::isGroup(dataBlob))
- {
- data->Data = dataBlob.Data;
- data->Length = dataBlob.Length;
- dataBlob.Data = NULL;
- dataBlob.Length = 0;
- return;
- }
-
- // Get the group for dataBlob
- SSGroup group(database(), dataBlob);
-
- // Decode the dataBlob, pass in the DL allocator.
- group->decodeDataBlob(dataBlob, cred, allocator(), *data);
-}
-
-SSGroup
-SSDbUniqueRecordImpl::group()
-{
- // Get the datablob for this record
- // @@@ Fixme so we don't need to call DbUniqueRecordImpl::get
- CssmDataContainer dataBlob(allocator());
- DbUniqueRecordImpl::get(NULL, &dataBlob);
- return SSGroup(database(), dataBlob);
-}