#include "securestorage.h"
#include <security_cdsa_client/genkey.h>
#include <security_utilities/osxcode.h>
#include <memory>
using namespace CssmClient;
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 (...)
{
return; }
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);
}
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::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();
}
DbUniqueRecord
SSDbImpl::insert(CSSM_DB_RECORDTYPE recordType,
const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes,
const CSSM_DATA *data)
{
return DbImpl::insert(recordType, attributes, data);
}
SSDbUniqueRecord
SSDbImpl::ssInsert(CSSM_DB_RECORDTYPE recordType,
const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes,
const CSSM_DATA *data,
const CSSM_RESOURCE_CONTROL_CONTEXT *rc)
{
CSSM_DL_DB_HANDLE dldbh;
passThrough(CSSM_APPLECSPDL_DB_GET_HANDLE, NULL,
reinterpret_cast<void **>(&dldbh));
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
{
SSDbUniqueRecord ssdbur = ssInsert(recordType, attributes, data, group, cred);
if (autoCommit)
{
check(CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_COMMIT, NULL, NULL));
CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
reinterpret_cast<const void *>(autoCommit), NULL);
}
return ssdbur;
}
catch(...)
{
try { group->deleteKey(cred); } catch (...) {}
if (autoCommit)
{
CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_ROLLBACK, NULL, NULL);
CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
reinterpret_cast<const void *>(autoCommit), NULL);
}
throw;
}
}
SSDbUniqueRecord
SSDbImpl::ssInsert(CSSM_DB_RECORDTYPE recordType,
const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes,
const CSSM_DATA *data, const SSGroup &group,
const CSSM_ACCESS_CREDENTIALS *cred)
{
CssmDataContainer dataBlob(allocator());
group->encodeDataBlob(data, cred, dataBlob);
return SSDbUniqueRecord(safe_cast<SSDbUniqueRecordImpl *>
(&(*DbImpl::insert(recordType, attributes, &dataBlob))));
}
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);
}
DbUniqueRecordImpl *
SSDbImpl::newDbUniqueRecord()
{
return new SSDbUniqueRecordImpl(Db(this));
}
CSSM_DB_NAME_ATTR(SSGroupImpl::kLabel, 6, (char*) "Label", 0, NULL, BLOB);
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));
CSP csp(this->csp());
Random random(csp, CSSM_ALGID_APPLE_YARROW);
random.generate(mLabel, (uint32)mLabel.Length);
reinterpret_cast<uint32 *>(mLabel.Data)[0] = h2n(uint32(kGroupMagic));
GenerateKey genKey(csp, CSSM_ALGID_3DES_3KEY, 192);
genKey.database(ssDb);
genKey.rcc(credAndAclEntry);
genKey(*this, KeySpec(CSSM_KEYUSE_ENCRYPT|CSSM_KEYUSE_DECRYPT,
CSSM_KEYATTR_PERMANENT|CSSM_KEYATTR_SENSITIVE,
mLabel));
activate();
}
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);
mLabel = CssmData(dataBlob.Data, kLabelSize);
if (*reinterpret_cast<const uint32 *>(mLabel.Data) != h2n (uint32(kGroupMagic)))
CssmError::throwMe(CSSMERR_DL_RECORD_NOT_FOUND);
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);
static_cast<CSSM_KEY &>(*this) =
*reinterpret_cast<const CSSM_KEY *>(keyData.Data);
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)
{
CssmData iv(&dataBlob.Data[kLabelSize], kIVSize);
CssmData cipherText(&dataBlob.Data[kLabelSize + kIVSize],
dataBlob.Length - (kLabelSize + kIVSize));
CssmDataContainer plainText1(allocator);
CssmDataContainer plainText2(allocator);
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);
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)
{
CSP csp(this->csp());
Random random(csp, CSSM_ALGID_APPLE_YARROW);
uint8 ivBuf[kIVSize];
CssmData iv(ivBuf, kIVSize);
random.generate(iv, kIVSize);
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));
const CssmData nothing;
const CssmData *plainText = data ? CssmData::overlay(data) : ¬hing;
CssmDataContainer cipherText1, cipherText2;
encrypt.encrypt(plainText, 1, &cipherText1, 1);
encrypt.final(cipherText2);
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::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;
CssmDataContainer dataBlob(allocator());
for (;;)
{
if (!DbDbCursorImpl::next(attrs, &dataBlob, uniqueId))
return false;
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)
{
database()->csp()->freeKey(*reinterpret_cast<CssmKey *>(dataBlob.Data));
if(!data) {
break;
}
} else {
break;
}
}
if(data) {
if (!SSGroupImpl::isGroup(dataBlob))
{
data->Data = dataBlob.Data;
data->Length = dataBlob.Length;
dataBlob.Data = NULL;
dataBlob.Length = 0;
return true;
}
SSGroup group(database(), dataBlob);
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;
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::SSDbUniqueRecordImpl(const Db &db)
: DbUniqueRecordImpl(db)
{
}
SSDbUniqueRecordImpl::~SSDbUniqueRecordImpl()
{
}
void
SSDbUniqueRecordImpl::deleteRecord()
{
deleteRecord(NULL);
}
void
SSDbUniqueRecordImpl::deleteRecord(const CSSM_ACCESS_CREDENTIALS *cred)
{
CssmDataContainer dataBlob(allocator());
DbAttributes attributes;
DbUniqueRecordImpl::get(&attributes, &dataBlob);
CSSM_KEY_PTR keyPtr = (CSSM_KEY_PTR) dataBlob.data();
DbUniqueRecordImpl::deleteRecord();
if (SSGroupImpl::isGroup(dataBlob))
try {
SSGroup group(database(), dataBlob);
group->deleteKey(cred);
} catch (const CssmError &err) {
switch (err.error) {
case CSSMERR_DL_RECORD_NOT_FOUND:
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;
}
CssmDataContainer oldDataBlob(allocator());
DbUniqueRecordImpl::get(NULL, &oldDataBlob);
if (!SSGroupImpl::isGroup(oldDataBlob))
{
DbUniqueRecordImpl::modify(recordType, attributes, data, modifyMode);
return;
}
SSGroup group(database(), oldDataBlob);
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;
}
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;
}
SSGroup group(database(), dataBlob);
group->decodeDataBlob(dataBlob, cred, allocator(), *data);
}
SSGroup
SSDbUniqueRecordImpl::group()
{
CssmDataContainer dataBlob(allocator());
DbUniqueRecordImpl::get(NULL, &dataBlob);
return SSGroup(database(), dataBlob);
}