#include "SSDatabase.h"
#include <security_cdsa_utilities/KeySchema.h>
using namespace CssmClient;
using namespace SecurityServer;
const char *const SSDatabaseImpl::DBBlobRelationName = "DBBlob";
SSDatabaseImpl::SSDatabaseImpl(ClientSession &inClientSession, const CssmClient::DL &dl,
const char *inDbName, const CSSM_NET_ADDRESS *inDbLocation)
: Db::Impl(dl, inDbName, inDbLocation), mClientSession(inClientSession), mSSDbHandle(noDb)
{
}
SSDatabaseImpl::~SSDatabaseImpl()
try
{
if (mSSDbHandle != noDb)
mClientSession.releaseDb(mSSDbHandle);
}
catch (...)
{
}
SSUniqueRecord
SSDatabaseImpl::insert(CSSM_DB_RECORDTYPE recordType,
const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes,
const CSSM_DATA *data, bool)
{
SSUniqueRecord uniqueId(SSDatabase(this));
check(CSSM_DL_DataInsert(handle(), recordType,
attributes,
data, uniqueId));
uniqueId->activate();
return uniqueId;
}
void
SSDatabaseImpl::authenticate(CSSM_DB_ACCESS_TYPE inAccessRequest,
const CSSM_ACCESS_CREDENTIALS *inAccessCredentials)
{
mClientSession.authenticateDb(dbHandle(), inAccessRequest,
AccessCredentials::overlay(inAccessCredentials));
}
void
SSDatabaseImpl::lock()
{
mClientSession.lock(dbHandle());
}
void
SSDatabaseImpl::unlock()
{
mClientSession.unlock(dbHandle());
}
void
SSDatabaseImpl::unlock(const CSSM_DATA &password)
{
mClientSession.unlock(dbHandle(), CssmData::overlay(password));
}
void
SSDatabaseImpl::stash()
{
mClientSession.stashDb(dbHandle());
}
void
SSDatabaseImpl::stashCheck()
{
mClientSession.stashDbCheck(dbHandle());
}
void
SSDatabaseImpl::getSettings(uint32 &outIdleTimeout, bool &outLockOnSleep)
{
DBParameters parameters;
mClientSession.getDbParameters(dbHandle(), parameters);
outIdleTimeout = parameters.idleTimeout;
outLockOnSleep = parameters.lockOnSleep;
}
void
SSDatabaseImpl::setSettings(uint32 inIdleTimeout, bool inLockOnSleep)
{
DBParameters parameters;
parameters.idleTimeout = inIdleTimeout;
parameters.lockOnSleep = inLockOnSleep;
mClientSession.setDbParameters(dbHandle(), parameters);
CssmDataContainer dbb(allocator());
mClientSession.encodeDb(mSSDbHandle, dbb, allocator());
getDbBlobId()->modify(DBBlobRelationID, NULL, &dbb, CSSM_DB_MODIFY_ATTRIBUTE_NONE);
}
bool
SSDatabaseImpl::isLocked()
{
return mClientSession.isLocked(dbHandle());
}
void
SSDatabaseImpl::changePassphrase(const CSSM_ACCESS_CREDENTIALS *cred)
{
mClientSession.changePassphrase(dbHandle(), AccessCredentials::overlay(cred));
CssmDataContainer dbb(allocator());
mClientSession.encodeDb(mSSDbHandle, dbb, allocator());
getDbBlobId()->modify(DBBlobRelationID, NULL, &dbb, CSSM_DB_MODIFY_ATTRIBUTE_NONE);
}
DbHandle
SSDatabaseImpl::dbHandle()
{
activate();
if (mForked()) {
CssmDataContainer dbb(allocator());
getDbBlobId(&dbb);
mSSDbHandle = mClientSession.decodeDb(mIdentifier,
AccessCredentials::overlay(accessCredentials()), dbb);
}
return mSSDbHandle;
}
void
SSDatabaseImpl::commonCreate(const DLDbIdentifier &dlDbIdentifier, bool &autoCommit)
{
mIdentifier = dlDbIdentifier;
autoCommit = true;
CSSM_APPLEDL_OPEN_PARAMETERS newOpenParameters =
{
sizeof(CSSM_APPLEDL_OPEN_PARAMETERS),
CSSM_APPLEDL_OPEN_PARAMETERS_VERSION,
CSSM_FALSE, 0 };
const CSSM_APPLEDL_OPEN_PARAMETERS *inOpenParameters =
reinterpret_cast<const CSSM_APPLEDL_OPEN_PARAMETERS *>(openParameters());
if (inOpenParameters)
{
switch (inOpenParameters->version)
{
case 1:
if (inOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS))
CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
newOpenParameters.mask = inOpenParameters->mask;
newOpenParameters.mode = inOpenParameters->mode;
case 0:
autoCommit = inOpenParameters->autoCommit == CSSM_FALSE ? false : true;
break;
default:
CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
}
}
openParameters(&newOpenParameters);
try
{
DbImpl::create();
openParameters(inOpenParameters);
}
catch (...)
{
openParameters(inOpenParameters);
throw;
}
createRelation(DBBlobRelationID, DBBlobRelationName,
0, (CSSM_DB_SCHEMA_ATTRIBUTE_INFO *)42,
0, (CSSM_DB_SCHEMA_INDEX_INFO *)42);
createRelation(CSSM_DL_DB_RECORD_PUBLIC_KEY, "CSSM_DL_DB_RECORD_PUBLIC_KEY",
KeySchema::KeySchemaAttributeCount, KeySchema::KeySchemaAttributeList,
KeySchema::KeySchemaIndexCount, KeySchema::KeySchemaIndexList);
createRelation(CSSM_DL_DB_RECORD_PRIVATE_KEY, "CSSM_DL_DB_RECORD_PRIVATE_KEY",
KeySchema::KeySchemaAttributeCount, KeySchema::KeySchemaAttributeList,
KeySchema::KeySchemaIndexCount, KeySchema::KeySchemaIndexList);
createRelation(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, "CSSM_DL_DB_RECORD_SYMMETRIC_KEY",
KeySchema::KeySchemaAttributeCount, KeySchema::KeySchemaAttributeList,
KeySchema::KeySchemaIndexCount, KeySchema::KeySchemaIndexList);
}
void
SSDatabaseImpl::create(const DLDbIdentifier &dlDbIdentifier)
{
try
{
bool autoCommit;
commonCreate(dlDbIdentifier, autoCommit);
DBParameters dbParameters;
memset(&dbParameters, 0, sizeof(DBParameters));
dbParameters.idleTimeout = kDefaultIdleTimeout;
dbParameters.lockOnSleep = kDefaultLockOnSleep;
const AccessCredentials *cred = NULL;
const AclEntryInput *owner = NULL;
if (resourceControlContext())
{
cred = AccessCredentials::overlay(resourceControlContext()->AccessCred);
owner = &AclEntryInput::overlay(resourceControlContext()->InitialAclEntry);
}
mSSDbHandle = mClientSession.createDb(dlDbIdentifier, cred, owner, dbParameters);
CssmDataContainer dbb(allocator());
mClientSession.encodeDb(mSSDbHandle, dbb, allocator());
Db::Impl::insert(DBBlobRelationID, NULL, &dbb);
if (autoCommit)
{
passThrough(CSSM_APPLEFILEDL_COMMIT, NULL);
passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
reinterpret_cast<const void *>(1));
}
}
catch(CssmError e)
{
if (e.error != CSSMERR_DL_DATASTORE_ALREADY_EXISTS)
{
DbImpl::deleteDb();
}
throw;
}
catch(...)
{
DbImpl::deleteDb();
throw;
}
}
void
SSDatabaseImpl::createWithBlob(const DLDbIdentifier &dlDbIdentifier, const CSSM_DATA &blob)
{
try
{
bool autoCommit;
commonCreate(dlDbIdentifier, autoCommit);
Db::Impl::insert(DBBlobRelationID, NULL, &blob);
if (autoCommit)
{
passThrough(CSSM_APPLEFILEDL_COMMIT, NULL);
passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
reinterpret_cast<const void *>(1));
}
}
catch(...)
{
DbImpl::deleteDb();
throw;
}
}
void
SSDatabaseImpl::open(const DLDbIdentifier &dlDbIdentifier)
{
mIdentifier = dlDbIdentifier;
Db::Impl::open();
CssmDataContainer dbb(allocator());
getDbBlobId(&dbb);
mSSDbHandle = mClientSession.decodeDb(dlDbIdentifier, AccessCredentials::overlay(accessCredentials()), dbb);
}
void
SSDatabaseImpl::recode(const CssmData &data, const CssmData &extraData)
{
passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 0);
try
{
CssmDataContainer dbb(allocator());
CssmClient::DbUniqueRecord dbBlobId = getDbBlobId(&dbb);
if (mForked()) {
mSSDbHandle = mClientSession.decodeDb(mIdentifier,
AccessCredentials::overlay(accessCredentials()), dbb);
}
dbb.clear();
DbHandle successfulHdl = mClientSession.authenticateDbsForSync(data, extraData);
SecurityServer::DbHandle clonedDbHandle =
mClientSession.recodeDbForSync(successfulHdl, mSSDbHandle);
DbCursor cursor(SSDatabase(this));
cursor->recordType(CSSM_DL_DB_RECORD_ALL_KEYS);
CssmDataContainer keyBlob(allocator());
CssmClient::DbUniqueRecord keyBlobId;
DbAttributes attributes;
while (cursor->next(&attributes, &keyBlob, keyBlobId))
{
CssmKey::Header header;
KeyHandle keyHandle =
mClientSession.decodeKey(mSSDbHandle, keyBlob, header);
CssmDataContainer newKeyBlob(mClientSession.returnAllocator);
mClientSession.recodeKey(mSSDbHandle, keyHandle, clonedDbHandle,
newKeyBlob);
mClientSession.releaseKey(keyHandle);
keyBlobId->modify(attributes.recordType(), NULL, &newKeyBlob,
CSSM_DB_MODIFY_ATTRIBUTE_NONE);
}
mClientSession.commitDbForSync(mSSDbHandle, clonedDbHandle,
dbb, allocator());
dbBlobId->modify(DBBlobRelationID, NULL, &dbb,
CSSM_DB_MODIFY_ATTRIBUTE_NONE);
passThrough(CSSM_APPLEFILEDL_COMMIT, NULL);
passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
reinterpret_cast<const void *>(1));
}
catch (...)
{
passThrough(CSSM_APPLEFILEDL_ROLLBACK, NULL);
passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
reinterpret_cast<const void *>(1));
throw;
}
}
void SSDatabaseImpl::getRecordIdentifier(CSSM_DB_UNIQUE_RECORD_PTR uniqueRecord, CSSM_DATA &recordID)
{
recordID.Length = sizeof (uint32) * kNumIDWords;
recordID.Data = (uint8*) allocator().malloc(recordID.Length);
uint32* dest = (uint32*) recordID.Data;
uint32* src = (uint32*) uniqueRecord->RecordIdentifier.Data;
dest[0] = htonl (src[0]);
dest[1] = htonl (src[1]);
dest[2] = htonl (src[2]);
dest[3] = 0;
}
void SSDatabaseImpl::copyBlob(CSSM_DATA &data)
{
CssmDataContainer dbb(allocator());
getDbBlobId(&dbb);
data.Data = dbb.Data;
data.Length = dbb.Length;
dbb.Data = NULL;
dbb.Length = 0;
}
DbUniqueRecordImpl *
SSDatabaseImpl::newDbUniqueRecord()
{
return new SSUniqueRecordImpl(SSDatabase(this));
}
CssmClient::DbUniqueRecord
SSDatabaseImpl::getDbBlobId(CssmDataContainer *dbb)
{
CssmClient::DbUniqueRecord dbBlobId;
DbCursor cursor(SSDatabase(this));
cursor->recordType(DBBlobRelationID);
if (!cursor->next(NULL, dbb, dbBlobId))
CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
return dbBlobId;
}
SSUniqueRecordImpl::SSUniqueRecordImpl(const SSDatabase &db)
: DbUniqueRecord::Impl(db)
{
}
SSUniqueRecordImpl::~SSUniqueRecordImpl()
{
}
SSDatabase
SSUniqueRecordImpl::database() const
{
return parent<SSDatabase>();
}