/*
 * 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.
 */


//
// SSDLSession.h - DL session for security server CSP/DL.
//
#include "SSDLSession.h"

#include "CSPDLPlugin.h"
#include "SSKey.h"
#include <security_cdsa_utilities/cssmbridge.h>
#include <Security/cssmapplePriv.h>

using namespace CssmClient;
using namespace SecurityServer;
using namespace std;

//
// SSDLSession -- Security Server DL session
//
SSDLSession::SSDLSession(CSSM_MODULE_HANDLE handle,
						 CSPDLPlugin &plug,
						 const CSSM_VERSION &version,
						 uint32 subserviceId,
						 CSSM_SERVICE_TYPE subserviceType,
						 CSSM_ATTACH_FLAGS attachFlags,
						 const CSSM_UPCALLS &upcalls,
						 DatabaseManager &databaseManager,
						 SSCSPDLSession &ssCSPDLSession)
: DLPluginSession(handle, plug, version, subserviceId, subserviceType,
				  attachFlags, upcalls, databaseManager),
  mSSCSPDLSession(ssCSPDLSession),
  mDL(Module(gGuidAppleFileDL, Cssm::standard())),
  mClientSession(Allocator::standard(), static_cast<PluginSession &>(*this))
{
	mClientSession.registerForAclEdits(SSCSPDLSession::didChangeKeyAclCallback, &mSSCSPDLSession);
	// @@@ mDL.allocator(*static_cast<DatabaseSession *>(this));
	mDL->allocator(allocator());
	mDL->version(version);
	mDL->subserviceId(subserviceId);
	mDL->flags(attachFlags);
}

SSDLSession::~SSDLSession()
{
	// @@@ What about a catch?
	StLock<Mutex> _1(mSSUniqueRecordLock);
	mSSUniqueRecordMap.clear();

	StLock<Mutex> _2(mDbHandleLock);
	DbHandleMap::iterator end = mDbHandleMap.end();
	for (DbHandleMap::iterator it = mDbHandleMap.begin(); it != end; ++it)
		it->second->close();

	mDbHandleMap.clear();
	mDL->detach();
}

// Utility functions
void
SSDLSession::GetDbNames(CSSM_NAME_LIST_PTR &outNameList)
{
	// @@@ Fix client lib
	CSSM_DL_GetDbNames(mDL->handle(), &outNameList);
}


void
SSDLSession::FreeNameList(CSSM_NAME_LIST &inNameList)
{
	// @@@ Fix client lib
	CSSM_DL_FreeNameList(mDL->handle(), &inNameList);
}


void
SSDLSession::DbDelete(const char *inDbName,
					  const CSSM_NET_ADDRESS *inDbLocation,
					  const AccessCredentials *inAccessCred)
{
	SSDatabase db(mClientSession, mDL, inDbName, inDbLocation);
	db->accessCredentials(inAccessCred);
	db->deleteDb();
}

// DbContext creation and destruction.
void
SSDLSession::DbCreate(const char *inDbName,
					  const CSSM_NET_ADDRESS *inDbLocation,
					  const CSSM_DBINFO &inDBInfo,
					  CSSM_DB_ACCESS_TYPE inAccessRequest,
					  const CSSM_RESOURCE_CONTROL_CONTEXT *inCredAndAclEntry,
					  const void *inOpenParameters,
					  CSSM_DB_HANDLE &outDbHandle)
{
	SSDatabase db(mClientSession, mDL, inDbName, inDbLocation);
	db->dbInfo(&inDBInfo);
	db->accessRequest(inAccessRequest);
	db->resourceControlContext(inCredAndAclEntry);
	db->openParameters(inOpenParameters);
	db->create(DLDbIdentifier(CssmSubserviceUid(plugin.myGuid(), &version(), subserviceId(),
												CSSM_SERVICE_DL | CSSM_SERVICE_CSP),
							  inDbName, inDbLocation));
	db->dbInfo(NULL);
	outDbHandle = makeDbHandle(db);
}

void
SSDLSession::CreateWithBlob(const char *DbName,
							const CSSM_NET_ADDRESS *DbLocation,
							const CSSM_DBINFO &DBInfo,
							CSSM_DB_ACCESS_TYPE AccessRequest,
							const void *OpenParameters,
							const CSSM_DATA &blob,
							CSSM_DB_HANDLE &DbHandle)
{
	SSDatabase db(mClientSession, mDL, DbName, DbLocation);
	db->dbInfo(&DBInfo);
	db->accessRequest(AccessRequest);
	db->resourceControlContext(NULL);
	db->openParameters(OpenParameters);
	db->createWithBlob(DLDbIdentifier(CssmSubserviceUid(plugin.myGuid(), &version(), subserviceId(),
									  CSSM_SERVICE_DL | CSSM_SERVICE_CSP),
									  DbName, DbLocation),
					   blob);
	db->dbInfo(NULL);
	DbHandle = makeDbHandle(db);
}

void
SSDLSession::DbOpen(const char *inDbName,
					const CSSM_NET_ADDRESS *inDbLocation,
					CSSM_DB_ACCESS_TYPE inAccessRequest,
					const AccessCredentials *inAccessCred,
					const void *inOpenParameters,
					CSSM_DB_HANDLE &outDbHandle)
{
	SSDatabase db(mClientSession, mDL, inDbName, inDbLocation);
	db->accessRequest(inAccessRequest);
	db->accessCredentials(inAccessCred);
	db->openParameters(inOpenParameters);
	db->open(DLDbIdentifier(CssmSubserviceUid(plugin.myGuid(), &version(), subserviceId(),
											  CSSM_SERVICE_DL | CSSM_SERVICE_CSP),
							inDbName, inDbLocation));
	outDbHandle = makeDbHandle(db);
}

// Operations using DbContext instances.
void
SSDLSession::DbClose(CSSM_DB_HANDLE inDbHandle)
{
	killDbHandle(inDbHandle)->close();
}

void
SSDLSession::CreateRelation(CSSM_DB_HANDLE inDbHandle,
							CSSM_DB_RECORDTYPE inRelationID,
							const char *inRelationName,
							uint32 inNumberOfAttributes,
							const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *inAttributeInfo,
							uint32 inNumberOfIndexes,
							const CSSM_DB_SCHEMA_INDEX_INFO &inIndexInfo)
{
	SSDatabase db = findDbHandle(inDbHandle);
	// @@@ Fix inAttributeInfo and inIndexInfo arguments (might be NULL if NumberOf = 0)
	db->createRelation(inRelationID, inRelationName,
					  inNumberOfAttributes, inAttributeInfo,
					  inNumberOfIndexes, &inIndexInfo);
}

void
SSDLSession::DestroyRelation(CSSM_DB_HANDLE inDbHandle,
							 CSSM_DB_RECORDTYPE inRelationID)
{
	// @@@ Check credentials.
	SSDatabase db = findDbHandle(inDbHandle);
	db->destroyRelation(inRelationID);
}

void
SSDLSession::Authenticate(CSSM_DB_HANDLE inDbHandle,
						  CSSM_DB_ACCESS_TYPE inAccessRequest,
						  const AccessCredentials &inAccessCred)
{
	SSDatabase db = findDbHandle(inDbHandle);
	db->authenticate(inAccessRequest, &inAccessCred);
}


void
SSDLSession::GetDbAcl(CSSM_DB_HANDLE inDbHandle,
					  const CSSM_STRING *inSelectionTag,
					  uint32 &outNumberOfAclInfos,
					  CSSM_ACL_ENTRY_INFO_PTR &outAclInfos)
{
	SSDatabase db = findDbHandle(inDbHandle);
	mClientSession.getDbAcl(db->dbHandle(),
		inSelectionTag ? *inSelectionTag : NULL,
		outNumberOfAclInfos, AclEntryInfo::overlayVar(outAclInfos), allocator());
}

void
SSDLSession::ChangeDbAcl(CSSM_DB_HANDLE inDbHandle,
						 const AccessCredentials &inAccessCred,
						 const CSSM_ACL_EDIT &inAclEdit)
{
	SSDatabase db = findDbHandle(inDbHandle);
	mClientSession.changeDbAcl(db->dbHandle(), inAccessCred, AclEdit::overlay(inAclEdit));
}

void
SSDLSession::GetDbOwner(CSSM_DB_HANDLE inDbHandle,
						CSSM_ACL_OWNER_PROTOTYPE &outOwner)
{
	SSDatabase db = findDbHandle(inDbHandle);
	mClientSession.getDbOwner(db->dbHandle(),
		AclOwnerPrototype::overlay(outOwner), allocator());
}

void
SSDLSession::ChangeDbOwner(CSSM_DB_HANDLE inDbHandle,
						   const AccessCredentials &inAccessCred,
						   const CSSM_ACL_OWNER_PROTOTYPE &inNewOwner)
{
	SSDatabase db = findDbHandle(inDbHandle);
	mClientSession.changeDbOwner(db->dbHandle(), inAccessCred,
		AclOwnerPrototype::overlay(inNewOwner));
}

void
SSDLSession::GetDbNameFromHandle(CSSM_DB_HANDLE inDbHandle,
                                 char **outDbName)
{
	SSDatabase db = findDbHandle(inDbHandle);
	// @@@ Fix this functions signature.
	db->name(*outDbName);
}

void
SSDLSession::DataInsert(CSSM_DB_HANDLE inDbHandle,
						CSSM_DB_RECORDTYPE inRecordType,
						const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributes,
						const CssmData *inData,
						CSSM_DB_UNIQUE_RECORD_PTR &outUniqueId)
{
	SSDatabase db = findDbHandle(inDbHandle);
	// @@@ Fix client lib.
    SSUniqueRecord uniqueId = db->insert(inRecordType, inAttributes, inData, true); // @@@ Fix me
	outUniqueId = makeSSUniqueRecord(uniqueId);
	// @@@ If this is a key do the right thing.
}

void
SSDLSession::DataDelete(CSSM_DB_HANDLE inDbHandle,
						const CSSM_DB_UNIQUE_RECORD &inUniqueRecordIdentifier)
{
	SSDatabase db = findDbHandle(inDbHandle);
	SSUniqueRecord uniqueId = findSSUniqueRecord(inUniqueRecordIdentifier);
	uniqueId->deleteRecord();
	// @@@ If this is a key do the right thing.
}


void
SSDLSession::DataModify(CSSM_DB_HANDLE inDbHandle,
						CSSM_DB_RECORDTYPE inRecordType,
						CSSM_DB_UNIQUE_RECORD &inoutUniqueRecordIdentifier,
						const CSSM_DB_RECORD_ATTRIBUTE_DATA *inAttributesToBeModified,
						const CssmData *inDataToBeModified,
						CSSM_DB_MODIFY_MODE inModifyMode)
{
	SSDatabase db = findDbHandle(inDbHandle);
	SSUniqueRecord uniqueId = findSSUniqueRecord(inoutUniqueRecordIdentifier);
	uniqueId->modify(inRecordType, inAttributesToBeModified, inDataToBeModified, inModifyMode);
	// @@@ If this is a key do the right thing.
}

CSSM_HANDLE
SSDLSession::DataGetFirst(CSSM_DB_HANDLE inDbHandle,
						  const CssmQuery *inQuery,
						  CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
						  CssmData *inoutData,
						  CSSM_DB_UNIQUE_RECORD_PTR &outUniqueRecord)
{
	SSDatabase db = findDbHandle(inDbHandle);
	CSSM_HANDLE resultsHandle = CSSM_INVALID_HANDLE;
    SSUniqueRecord uniqueId(db);

	// Setup so we always retrive the attributes even if the client
	// doesn't want them so we can figure out if we just retrived a key.
	CSSM_DB_RECORD_ATTRIBUTE_DATA attributes;
	CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR pAttributes;
	if (inoutAttributes)
		pAttributes = inoutAttributes;
	else
	{
		pAttributes = &attributes;
		memset(pAttributes, 0, sizeof(attributes));
	}

	// Retrive the record.
	CSSM_RETURN result = CSSM_DL_DataGetFirst(db->handle(), inQuery, &resultsHandle,
											  pAttributes, inoutData, uniqueId);
	if (result)
	{
		if (result == CSSMERR_DL_ENDOFDATA)
			return CSSM_INVALID_HANDLE;

		CssmError::throwMe(result);
	}

	uniqueId->activate();

	// If we the client didn't ask for data then it doesn't matter
	// if this record is a key or not, just return it.
	if (inoutData)
	{
		if (pAttributes->DataRecordType == CSSM_DL_DB_RECORD_PUBLIC_KEY
			|| pAttributes->DataRecordType == CSSM_DL_DB_RECORD_PRIVATE_KEY
			|| pAttributes->DataRecordType == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
		{
			// This record is a key, do the right thing (tm).
			// Allocate storage for the key.
			CssmKey *outKey = allocator().alloc<CssmKey>();
			new SSKey(*this, *outKey, db, uniqueId, pAttributes->DataRecordType, *inoutData);

			// Free the data we retrived (keyblob)
			allocator().free(inoutData->Data);

			// Set the length and data on the data we return to the client
			inoutData->Length = sizeof(*outKey);
			inoutData->Data = reinterpret_cast<uint8 *>(outKey);			
		}
	}

	outUniqueRecord = makeSSUniqueRecord(uniqueId);
	return resultsHandle;
}

bool
SSDLSession::DataGetNext(CSSM_DB_HANDLE inDbHandle,
						 CSSM_HANDLE inResultsHandle,
						 CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
						 CssmData *inoutData,
						 CSSM_DB_UNIQUE_RECORD_PTR &outUniqueRecord)
{
	// @@@ If this is a key do the right thing.
	SSDatabase db = findDbHandle(inDbHandle);
    SSUniqueRecord uniqueId(db);

	// Setup so we always retrive the attributes even if the client
	// doesn't want them so we can figure out if we just retrived a key.
	CSSM_DB_RECORD_ATTRIBUTE_DATA attributes;
	CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR pAttributes;
	if (inoutAttributes)
		pAttributes = inoutAttributes;
	else
	{
		pAttributes = &attributes;
		memset(pAttributes, 0, sizeof(attributes));
	}

	CSSM_RETURN result = CSSM_DL_DataGetNext(db->handle(), inResultsHandle,
											 inoutAttributes, inoutData, uniqueId);
	if (result)
	{
		if (result == CSSMERR_DL_ENDOFDATA)
			return false;

		CssmError::throwMe(result);
	}

	uniqueId->activate();

	// If we the client didn't ask for data then it doesn't matter
	// if this record is a key or not, just return it.
	if (inoutData)
	{
		if (pAttributes->DataRecordType == CSSM_DL_DB_RECORD_PUBLIC_KEY
			|| pAttributes->DataRecordType == CSSM_DL_DB_RECORD_PRIVATE_KEY
			|| pAttributes->DataRecordType == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
		{
			// This record is a key, do the right thing (tm).
			// Allocate storage for the key.
			CssmKey *outKey = allocator().alloc<CssmKey>();
			new SSKey(*this, *outKey, db, uniqueId, pAttributes->DataRecordType, *inoutData);

			// Free the data we retrived (keyblob)
			allocator().free(inoutData->Data);

			// Set the length and data on the data we return to the client
			inoutData->Length = sizeof(*outKey);
			inoutData->Data = reinterpret_cast<uint8 *>(outKey);			
		}
	}

	outUniqueRecord = makeSSUniqueRecord(uniqueId);

	return true;
}

void
SSDLSession::DataAbortQuery(CSSM_DB_HANDLE inDbHandle,
							CSSM_HANDLE inResultsHandle)
{
	// @@@ If this is a key do the right thing.
	SSDatabase db = findDbHandle(inDbHandle);
	CSSM_RETURN result = CSSM_DL_DataAbortQuery(db->handle(), inResultsHandle);
	if (result)
		CssmError::throwMe(result);
}

void
SSDLSession::DataGetFromUniqueRecordId(CSSM_DB_HANDLE inDbHandle,
									   const CSSM_DB_UNIQUE_RECORD &inUniqueRecord,
									   CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR inoutAttributes,
									   CssmData *inoutData)
{
	SSDatabase db = findDbHandle(inDbHandle);
	const SSUniqueRecord uniqueId = findSSUniqueRecord(inUniqueRecord);
	
	// Setup so we always retrive the attributes even if the client
	// doesn't want them so we can figure out if we just retrived a key.
	CSSM_DB_RECORD_ATTRIBUTE_DATA attributes;
	CSSM_DB_RECORD_ATTRIBUTE_DATA_PTR pAttributes;
	if (inoutAttributes)
		pAttributes = inoutAttributes;
	else
	{
		pAttributes = &attributes;
		memset(pAttributes, 0, sizeof(attributes));
	}

	CSSM_RETURN result = CSSM_DL_DataGetFromUniqueRecordId(db->handle(), 
		uniqueId, pAttributes, inoutData);
	if (result)
		CssmError::throwMe(result);

	if (inoutData)
	{
		if (pAttributes->DataRecordType == CSSM_DL_DB_RECORD_PUBLIC_KEY
			|| pAttributes->DataRecordType == CSSM_DL_DB_RECORD_PRIVATE_KEY
			|| pAttributes->DataRecordType == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
		{
			// This record is a key, do the right thing (tm).
			// Allocate storage for the key.
			CssmKey *outKey = allocator().alloc<CssmKey>();
			new SSKey(*this, *outKey, db, uniqueId, pAttributes->DataRecordType, *inoutData);

			// Free the data we retrived (keyblob)
			allocator().free(inoutData->Data);

			// Set the length and data on the data we return to the client
			inoutData->Length = sizeof(*outKey);
			inoutData->Data = reinterpret_cast<uint8 *>(outKey);			
		}
	}
}

void
SSDLSession::FreeUniqueRecord(CSSM_DB_HANDLE inDbHandle,
							  CSSM_DB_UNIQUE_RECORD &inUniqueRecordIdentifier)
{
	killSSUniqueRecord(inUniqueRecordIdentifier);
}

static const uint32 kGenericAttributeNames[] =
{
	'cdat', 'mdat', 'desc', 'icmt', 'crtr', 'type', 'scrp', 7, 8, 'invi', 'nega', 'cusi', 'prot', 'acct', 'svce',
	'gena'
};

const uint32 kNumGenericAttributes = sizeof (kGenericAttributeNames) / sizeof (uint32);

static const uint32 kApplesharePasswordNames[] =
{
	'cdat', 'mdat', 'desc', 'icmt', 'crtr', 'type', 'scrp', 7, 8, 'invi', 'nega', 'cusi', 'prot', 'acct', 'vlme',
	'srvr', 'ptcl', 'addr', 'ssig'
};

const uint32 kNumApplesharePasswordAttributes = sizeof (kApplesharePasswordNames) / sizeof (uint32);

static const uint32 kInternetPasswordNames[] =
{
	'cdat', 'mdat', 'desc', 'icmt', 'crtr', 'type', 'scrp', 7, 8, 'invi', 'nega', 'cusi', 'prot', 'acct', 'sdmn',
	'srvr', 'ptcl', 'atyp', 'port', 'path'
};

const uint32 kNumInternetPasswordAttributes = sizeof (kInternetPasswordNames) / sizeof (uint32);

const uint32 kKeyAttributeNames[] =
{
	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26
};

const uint32 kNumKeyAttributes = sizeof (kKeyAttributeNames) / sizeof (uint32);

const uint32 kCertificateAttributeNames[] =
{
	'ctyp', 'cenc', 'labl', 'alis', 'subj', 'issu', 'snbr', 'skid', 'hpky'
};

const uint32 kNumCertificateAttributes = sizeof (kCertificateAttributeNames) / sizeof (uint32);

const unsigned kSymmetricKeyLabel = 6; // record id for the symmetric key
const unsigned kLabelSize = 20;
const unsigned kNumSymmetricAttributes = 27; // number of attributes to request

static void appendUInt32ToData (const uint32 value, CssmDataContainer &data)
{
	data.append (CssmPolyData (uint32 (htonl (value))));
}

static inline uint32 GetUInt32AtFinger (uint8 *&finger)
{
	uint32 a = ntohl ((finger[0] << 24) | (finger[1] << 16) | (finger[2] << 8) | finger[3]);
	finger += sizeof (uint32);
	return a;
}

void
SSDLSession::unwrapAttributesAndData (uint32 &numAttributes,
									  CSSM_DB_ATTRIBUTE_DATA_PTR &attributes,
									  CSSM_DATA &data,
									  CSSM_DATA &input)
{
	// get the number of attributes
	uint8* finger = input.Data;
	numAttributes = GetUInt32AtFinger (finger);
	
	// compute the end of the data for sanity checking later
	uint8* maximum = input.Data + input.Length;
	
	// make the attribute array
	attributes = (CSSM_DB_ATTRIBUTE_DATA*) allocator ().malloc (numAttributes * sizeof (CSSM_DB_ATTRIBUTE_DATA));
	
	// for each attribute, retrieve the name format, name, type, and number of values
	unsigned i;
	for (i = 0; i < numAttributes; ++i)
	{
		attributes[i].Info.AttributeNameFormat = GetUInt32AtFinger (finger);
		attributes[i].Info.Label.AttributeID = GetUInt32AtFinger (finger);
		attributes[i].Info.AttributeFormat = GetUInt32AtFinger (finger);
		attributes[i].NumberOfValues = GetUInt32AtFinger (finger);
		
		// for each value, get the length and data
		attributes[i].Value = (CSSM_DATA*) allocator ().malloc (sizeof (CSSM_DATA) * attributes[i].NumberOfValues);
		unsigned j;
		for (j = 0; j < attributes[i].NumberOfValues; ++j)
		{
			attributes[i].Value[j].Length = GetUInt32AtFinger (finger);
			if (attributes[i].Value[j].Length != 0)
			{
				// sanity check what we are about to do
				if (finger > maximum || finger + attributes[i].Value[j].Length > maximum)
				{
					CssmError::throwMe (CSSM_ERRCODE_INVALID_POINTER);
				}

				attributes[i].Value[j].Data = (uint8*) allocator ().malloc (attributes[i].Value[j].Length);
				memmove (attributes[i].Value[j].Data, finger, attributes[i].Value[j].Length);
				finger += attributes[i].Value[j].Length;
			}
			else
			{
				attributes[i].Value[j].Data = NULL;
			}
		}
	}
	
	// get the data
	data.Length = GetUInt32AtFinger (finger);
	if (data.Length != 0)
	{
		// sanity check the pointer
		if (finger + data.Length > maximum)
		{
			CssmError::throwMe (CSSM_ERRCODE_INVALID_POINTER);
		}

		data.Data = (uint8*) allocator ().malloc (data.Length);
		memmove (data.Data, finger, data.Length);
		finger += data.Length;
	}
	else
	{
		data.Data = NULL;
	}
}

void
SSDLSession::getWrappedAttributesAndData (SSDatabase &db,
										  CSSM_DB_RECORDTYPE recordType,
										  CSSM_DB_UNIQUE_RECORD_PTR recordPtr,
										  CssmDataContainer &output,
										  CSSM_DATA *dataBlob)
{
	// figure out which attributes to use
	const uint32* attributeNameArray;
	uint32 numAttributeNames;
	
	switch (recordType)
	{
		case CSSM_DL_DB_RECORD_GENERIC_PASSWORD:
		{
			attributeNameArray = kGenericAttributeNames;
			numAttributeNames = kNumGenericAttributes;
			break;
		}
		
		case CSSM_DL_DB_RECORD_INTERNET_PASSWORD:
		{
			attributeNameArray = kInternetPasswordNames;
			numAttributeNames = kNumInternetPasswordAttributes;
			break;
		}
		
		case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD:
		{
			attributeNameArray = kApplesharePasswordNames;
			numAttributeNames = kNumApplesharePasswordAttributes;
			break;
		}
		
		case CSSM_DL_DB_RECORD_X509_CERTIFICATE:
		{
			attributeNameArray = kCertificateAttributeNames;
			numAttributeNames = kNumCertificateAttributes;
			break;
		}
		
		case CSSM_DL_DB_RECORD_PUBLIC_KEY:
		case CSSM_DL_DB_RECORD_PRIVATE_KEY:
		case CSSM_DL_DB_RECORD_SYMMETRIC_KEY:
		{
			attributeNameArray = kKeyAttributeNames;
			numAttributeNames = kNumKeyAttributes;
			break;
		}

		default:
		{
			CssmError::throwMe (CSSMERR_DL_FUNCTION_NOT_IMPLEMENTED);
		}
	}
	
	// make the attribute array
	size_t arraySize = numAttributeNames * sizeof (CSSM_DB_ATTRIBUTE_DATA);
	
	CSSM_DB_ATTRIBUTE_DATA_PTR attributes =
		(CSSM_DB_ATTRIBUTE_DATA_PTR) allocator ().malloc (arraySize);
	
	// initialize the array
	memset (attributes, 0, arraySize);
	unsigned i;
	for (i = 0; i < numAttributeNames; ++i)
	{
		attributes[i].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
		attributes[i].Info.Label.AttributeID = attributeNameArray[i];
	}
	
	// make the attribute record
	CSSM_DB_RECORD_ATTRIBUTE_DATA attrData;
	attrData.DataRecordType = recordType;
	attrData.SemanticInformation = 0;
	attrData.NumberOfAttributes = numAttributeNames;
	attrData.AttributeData = attributes;
	
	// get the data
	CssmDataContainer data;
	CSSM_RETURN result = CSSM_DL_DataGetFromUniqueRecordId (db->handle (),
															recordPtr,
															&attrData,
															&data);
	if (result != 0)
	{
		CssmError::throwMe (result);
	}
	
	// wrap the data -- write the number of attributes
	appendUInt32ToData (numAttributeNames, output);
	
	// for each attribute, write the type and number of values
	for (i = 0; i < numAttributeNames; ++i)
	{
		appendUInt32ToData (attributes[i].Info.AttributeNameFormat, output);
		appendUInt32ToData (attributes[i].Info.Label.AttributeID, output);
		appendUInt32ToData (attributes[i].Info.AttributeFormat, output);
		appendUInt32ToData (attributes[i].NumberOfValues, output);
		
		// for each value, write the name format, name, length and the data
		unsigned j;
		for (j = 0; j < attributes[i].NumberOfValues; ++j)
		{
			appendUInt32ToData (attributes[i].Value[j].Length, output);
			if (attributes[i].Value[j].Length != 0)
			{
				switch (attributes[i].Info.AttributeFormat)
				{
					default:
					{
						output.append (CssmPolyData (attributes[i].Value[j]));
						break;
					}
					
					case CSSM_DB_ATTRIBUTE_FORMAT_SINT32:
					case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
					{
						uint32 n = htonl (*(uint32*) attributes[i].Value[j].Data);
						CSSM_DATA d;
						d.Length = sizeof (uint32);
						d.Data = (uint8*) &n;
						output.append (CssmPolyData (d));
						break;
					}
				}
			}
		}
	}
	
	// write the length of the data
	appendUInt32ToData (data.Length, output);
	
	// write the data itself
	if (data.Length != 0)
	{
		output.append (CssmPolyData (data));
	}
	
	// clean up
	for (i = 0; i < numAttributeNames; ++i)
	{
		unsigned j;
		for (j = 0; j < attributes[i].NumberOfValues; ++j)
		{
			allocator ().free (attributes[i].Value[j].Data);
		}
		
		allocator ().free (attributes[i].Value);
	}
	
	allocator ().free (attributes);
	
	// copy out the data if the caller needs it
	if (dataBlob)
	{
		dataBlob->Data = data.Data;
		dataBlob->Length = data.Length;
		data.Data = NULL;
		data.Length = 0;
	}
}

void
SSDLSession::getUniqueIdForSymmetricKey (SSDatabase &db, CSSM_DATA &label,
										 CSSM_DB_UNIQUE_RECORD_PTR &uniqueRecord)
{
	// set up a query to get the key
	CSSM_SELECTION_PREDICATE predicate;
	predicate.DbOperator = CSSM_DB_EQUAL;
	predicate.Attribute.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
	predicate.Attribute.Info.Label.AttributeID = kSymmetricKeyLabel;
	predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
	predicate.Attribute.NumberOfValues = 1;
	// the label of the corresponding key is the first 20 bytes of the blob we returned
	predicate.Attribute.Value = &label;

	CSSM_QUERY query;
	query.RecordType = CSSM_DL_DB_RECORD_SYMMETRIC_KEY;
	query.Conjunctive = CSSM_DB_NONE;
	query.NumSelectionPredicates = 1;
	query.SelectionPredicate = &predicate;
	
	// fill out the record data
	CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttributeData;
	recordAttributeData.DataRecordType = CSSM_DL_DB_RECORD_SYMMETRIC_KEY;
	recordAttributeData.SemanticInformation = 0;
	recordAttributeData.NumberOfAttributes = 0;
	recordAttributeData.AttributeData = NULL;
	
	// get the data
	CSSM_HANDLE handle;
	CSSM_RETURN result = CSSM_DL_DataGetFirst (db->handle (), &query, &handle, &recordAttributeData, NULL,
											   &uniqueRecord);
	if (result)
	{
		CssmError::throwMe (result);
	}

	// clean up
	CSSM_DL_DataAbortQuery (db->handle (), handle);
}

void
SSDLSession::getCorrespondingSymmetricKey (SSDatabase &db, CSSM_DATA &labelData, CssmDataContainer &data)
{
	// get the unique ID
	CSSM_DB_UNIQUE_RECORD_PTR uniqueRecord;
	getUniqueIdForSymmetricKey (db, labelData, uniqueRecord);

	// from this. get the wrapped attributes and data
	getWrappedAttributesAndData (db, CSSM_DL_DB_RECORD_SYMMETRIC_KEY, uniqueRecord, data, NULL);
	
	// clean up after the query
	CSSM_DL_FreeUniqueRecord (db->handle (), uniqueRecord);
}

void SSDLSession::doGetWithoutEncryption (SSDatabase &db, const void *inInputParams, void **outOutputParams)
{
	CSSM_APPLECSPDL_DB_GET_WITHOUT_ENCRYPTION_PARAMETERS* params =
		(CSSM_APPLECSPDL_DB_GET_WITHOUT_ENCRYPTION_PARAMETERS*) inInputParams;
		
	SSUniqueRecord uniqueID = findSSUniqueRecord(*(params->uniqueID));
	
	CSSM_DATA *outputData = (CSSM_DATA*) outOutputParams;
	CssmDataContainer output;
	
	// get the record type and requested attributes from the DL
	CssmDataContainer data;
	CSSM_RETURN result = CSSM_DL_DataGetFromUniqueRecordId(db->handle(),
														   uniqueID,
														   params->attributes,
														   NULL);
	
	if (result)
	{
		CssmError::throwMe(result);
	}

	// get the real data and all of the attributes from the DL
	CssmDataContainer blobData;
	getWrappedAttributesAndData (db, params->attributes->DataRecordType, uniqueID, data, &blobData);
	
	// write out the data blob
	appendUInt32ToData (data.Length, output);
	output.append (CssmPolyData (data));
	
	// figure out what we need to do with the key blob
	CssmDataContainer key;
	switch (params->attributes->DataRecordType)
	{
		case CSSM_DL_DB_RECORD_GENERIC_PASSWORD:
		case CSSM_DL_DB_RECORD_INTERNET_PASSWORD:
		case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD:
		{
			// the label is the first kLabelSize bytes of the resultant data blob
			CSSM_DATA label = {kLabelSize, blobData.Data};

			// get the key
			getCorrespondingSymmetricKey (db, label, key);
		}
		break;
		
		default:
		{
			break;
		}
	}
	
	
	// write out the length of the key blob
	appendUInt32ToData (key.Length, output);

	if (key.Length != 0)
	{
		// write the key
		output.append (CssmPolyData (key));
	}
	
	// copy out the results
	outputData->Data = output.Data;
	output.Data = NULL;
	outputData->Length = output.Length;
	output.Length = NULL;
}

void
SSDLSession::cleanupAttributes (uint32 numAttributes, CSSM_DB_ATTRIBUTE_DATA_PTR attributes)
{
	unsigned i;
	for (i = 0; i < numAttributes; ++i)
	{
		unsigned j;
		for (j = 0; j < attributes[i].NumberOfValues; ++j)
		{
			free (attributes[i].Value[j].Data);
		}
		
		free (attributes[i].Value);
	}
	
	free (attributes);
}

void
SSDLSession::doModifyWithoutEncryption (SSDatabase &db, const void* inInputParams, void** outOutputParams)
{
	CSSM_RETURN result;
	CSSM_APPLECSPDL_DB_MODIFY_WITHOUT_ENCRYPTION_PARAMETERS* params =
		(CSSM_APPLECSPDL_DB_MODIFY_WITHOUT_ENCRYPTION_PARAMETERS*) inInputParams;
	
	// extract the data for this modify.
	uint8* finger = params->data->Data;
	CSSM_DATA data;
	data.Length = GetUInt32AtFinger (finger);
	data.Data = finger;
	if (data.Length + sizeof (UInt32) > params->data->Length)
	{
		CssmError::throwMe (CSSM_ERRCODE_INVALID_POINTER);
	}
	
	// point to the key
	finger += data.Length;
	
	// reconstruct the attributes and data
	uint32 numAttributes;
	CSSM_DB_ATTRIBUTE_DATA_PTR attributes;
	CssmDataContainer dataBlob;
	
	unwrapAttributesAndData (numAttributes, attributes, dataBlob, data);
	
	CSSM_DB_RECORD_ATTRIBUTE_DATA attrData;
	attrData.DataRecordType = params->attributes->DataRecordType;
	attrData.SemanticInformation = 0;
	attrData.NumberOfAttributes = numAttributes;
	attrData.AttributeData = attributes;
	
	// get the unique ID for this record (from the db's perspective)
	SSUniqueRecord uniqueID = findSSUniqueRecord(*(params->uniqueID));
	CSSM_DB_UNIQUE_RECORD *uniqueIDPtr = uniqueID; // for readability.  There's cast overloading
												   // going on here.
	
	switch (attrData.DataRecordType)
	{
		case CSSM_DL_DB_RECORD_GENERIC_PASSWORD:
		case CSSM_DL_DB_RECORD_INTERNET_PASSWORD:
		case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD:
		{
			// read off the data so that we can update the key
			CssmDataContainer oldData;
			result = CSSM_DL_DataGetFromUniqueRecordId (db->handle(),
														uniqueIDPtr,
														NULL,
														&oldData);
			if (result)
			{
				CssmError::throwMe (result);
			}
	
			CSSM_DB_MODIFY_MODE modifyMode = params->modifyMode;

			// parse the key data blob
			CssmDataContainer keyBlob;
			data.Length = GetUInt32AtFinger (finger);
			data.Data = finger;
				
			CSSM_DB_RECORD_ATTRIBUTE_DATA* attrDataPtr = NULL;
			CSSM_DB_RECORD_ATTRIBUTE_DATA attrData;
			
			CSSM_DATA labelData = {kLabelSize, oldData.Data};
			CSSM_DB_UNIQUE_RECORD_PTR recordID;
			getUniqueIdForSymmetricKey (db, labelData, recordID);
				
			CSSM_DB_ATTRIBUTE_DATA_PTR keyAttributes;
			uint32 numKeyAttributes;
			unwrapAttributesAndData (numKeyAttributes, keyAttributes, keyBlob, data);
				
			// make the attribute data
			attrData.DataRecordType = params->recordType;
			attrData.SemanticInformation = 0;
			attrData.NumberOfAttributes = numKeyAttributes;
			attrData.AttributeData = keyAttributes;
			
			attrDataPtr = &attrData;
		
			result = CSSM_DL_DataModify (db->handle(),
							 CSSM_DL_DB_RECORD_SYMMETRIC_KEY,
							 recordID,
							 attrDataPtr,
							 &keyBlob,
							 modifyMode);

			// clean up
			CSSM_DL_FreeUniqueRecord (db->handle (), recordID);
			
			cleanupAttributes (numKeyAttributes, keyAttributes);
			break;
		}
		
		default:
		{
			break;
		}
	}

	// save off the new data
	result = CSSM_DL_DataModify(db->handle(),
								params->recordType,
								uniqueIDPtr,
								&attrData,
								&dataBlob,
								params->modifyMode);

	// clean up
	cleanupAttributes (numAttributes, attributes);
	
	if (result)
	{
		CssmError::throwMe(result);
	}

}

void
SSDLSession::doInsertWithoutEncryption (SSDatabase &db, const void* inInputParams, void** outOutputParams)
{
	CSSM_RETURN result;

	CSSM_APPLECSPDL_DB_INSERT_WITHOUT_ENCRYPTION_PARAMETERS* params =
		(CSSM_APPLECSPDL_DB_INSERT_WITHOUT_ENCRYPTION_PARAMETERS*) inInputParams;
	
	// extract the data for this insert.
	uint8* finger = params->data.Data;
	CSSM_DATA data;
	data.Length = GetUInt32AtFinger (finger);
	data.Data = finger;
	finger += data.Length;
	
	// reconstruct the attributes and data
	uint32 numAttributes;
	CSSM_DB_ATTRIBUTE_DATA_PTR attributes;
	CSSM_DATA dataBlob;
	
	unwrapAttributesAndData (numAttributes, attributes, dataBlob, data);
	
	// make the attribute data
	CSSM_DB_RECORD_ATTRIBUTE_DATA attrData;
	attrData.DataRecordType = params->recordType;
	attrData.SemanticInformation = 0;
	attrData.NumberOfAttributes = numAttributes;
	attrData.AttributeData = attributes;
	
	// insert into the database
	SSUniqueRecord uniqueID (db);
	result = CSSM_DL_DataInsert (db->handle(), params->recordType,
								 &attrData,
								 &dataBlob,
								 uniqueID);

	// cleanup
	allocator ().free (dataBlob.Data);
	cleanupAttributes (numAttributes, attributes);

	// attach into the CSP/DL mechanism
	CSSM_DB_UNIQUE_RECORD_PTR newRecord = makeSSUniqueRecord(uniqueID);
	*(CSSM_DB_UNIQUE_RECORD_PTR*) outOutputParams = newRecord;
	
	if (result)
	{
		CssmError::throwMe(result);
	}

	// Get the key data for this insert
	data.Length = GetUInt32AtFinger (finger);
	if (data.Length != 0)
	{
		data.Data = finger;

		// parse the key data blob
		unwrapAttributesAndData (numAttributes, attributes, dataBlob, data);
		
		// make the attribute data
		CSSM_DB_RECORD_ATTRIBUTE_DATA attrData;
		attrData.DataRecordType = params->recordType;
		attrData.SemanticInformation = 0;
		attrData.NumberOfAttributes = numAttributes;
		attrData.AttributeData = attributes;

		// insert the key data into the symmetric key table
		CSSM_DB_UNIQUE_RECORD_PTR uniqueRecord;
		result = CSSM_DL_DataInsert (db->handle(), CSSM_DL_DB_RECORD_SYMMETRIC_KEY, &attrData, &dataBlob,
									 &uniqueRecord);
		if (result)
		{
			CssmError::throwMe (result);
		}
		
		// clean up after inserting the key
		CSSM_DL_FreeUniqueRecord (db->handle (), uniqueRecord);
		allocator ().free (dataBlob.Data);
		cleanupAttributes (numAttributes, attributes);
	}
}

void
SSDLSession::doConvertRecordIdentifier (SSDatabase &db, const void *inInputParams, void **outOutputParams)
{
	SSUniqueRecord uniqueId (db);

	// clone the unique record
	CSSM_DB_UNIQUE_RECORD_PTR clone = (CSSM_DB_UNIQUE_RECORD_PTR) allocator ().malloc (sizeof (CSSM_DB_UNIQUE_RECORD));
	*clone = *(CSSM_DB_UNIQUE_RECORD_PTR) inInputParams;

	// set the value of the unique record
	uniqueId->setUniqueRecordPtr (clone);
	
	// byte swap the retrieved record pointer to host order
	uint32* idArray = (uint32*) clone->RecordIdentifier.Data;
	idArray[0] = ntohl (idArray[0]);
	idArray[1] = ntohl (idArray[1]);
	idArray[2] = ntohl (idArray[2]);
	
	CSSM_DB_UNIQUE_RECORD_PTR newRecord = makeSSUniqueRecord(uniqueId);
	*(CSSM_DB_UNIQUE_RECORD_PTR*) outOutputParams = newRecord;
}

void
SSDLSession::PassThrough(CSSM_DB_HANDLE inDbHandle,
						 uint32 inPassThroughId,
						 const void *inInputParams,
						 void **outOutputParams)
{
	if (inPassThroughId == CSSM_APPLECSPDL_DB_CREATE_WITH_BLOB)
	{
		CSSM_APPLE_CSPDL_DB_CREATE_WITH_BLOB_PARAMETERS* params = (CSSM_APPLE_CSPDL_DB_CREATE_WITH_BLOB_PARAMETERS*) inInputParams;
		CreateWithBlob(params->dbName, params->dbLocation, *params->dbInfo, params->accessRequest, params->openParameters, *params->blob,
					   * (CSSM_DB_HANDLE*) outOutputParams);
		return;
	}
	
	SSDatabase db = findDbHandle(inDbHandle);
	switch (inPassThroughId)
	{
		case CSSM_APPLECSPDL_DB_LOCK:
			db->lock();
			break;
		case CSSM_APPLECSPDL_DB_UNLOCK:
			if (inInputParams)
				db->unlock(*reinterpret_cast<const CSSM_DATA *>(inInputParams));
			else
				db->unlock();
			break;
		case CSSM_APPLECSPDL_DB_GET_SETTINGS:
		{
			if (!outOutputParams)
				CssmError::throwMe(CSSM_ERRCODE_INVALID_OUTPUT_POINTER);

			CSSM_APPLECSPDL_DB_SETTINGS_PARAMETERS_PTR params =
				allocator().alloc<CSSM_APPLECSPDL_DB_SETTINGS_PARAMETERS>();
			try
			{
				uint32 idleTimeout;
				bool lockOnSleep;
				db->getSettings(idleTimeout, lockOnSleep);
				params->idleTimeout = idleTimeout;
				params->lockOnSleep = lockOnSleep;
			}
			catch(...)
			{
				allocator().free(params);
				throw;
			}
			*reinterpret_cast<CSSM_APPLECSPDL_DB_SETTINGS_PARAMETERS_PTR *>(outOutputParams) = params;
			break;
		}
		case CSSM_APPLECSPDL_DB_SET_SETTINGS:
		{
			if (!inInputParams)
				CssmError::throwMe(CSSM_ERRCODE_INVALID_INPUT_POINTER);

			const CSSM_APPLECSPDL_DB_SETTINGS_PARAMETERS *params =
				reinterpret_cast<const CSSM_APPLECSPDL_DB_SETTINGS_PARAMETERS *>(inInputParams);
			db->setSettings(params->idleTimeout, params->lockOnSleep);
			break;
		}
		case CSSM_APPLECSPDL_DB_IS_LOCKED:
		{
			if (!outOutputParams)
				CssmError::throwMe(CSSM_ERRCODE_INVALID_OUTPUT_POINTER);

			CSSM_APPLECSPDL_DB_IS_LOCKED_PARAMETERS_PTR params =
				allocator().alloc<CSSM_APPLECSPDL_DB_IS_LOCKED_PARAMETERS>();
			try
			{
				params->isLocked = db->isLocked();
			}
			catch(...)
			{
				allocator().free(params);
				throw;
			}
			*reinterpret_cast<CSSM_APPLECSPDL_DB_IS_LOCKED_PARAMETERS_PTR *>(outOutputParams) = params;
			break;
		}
		case CSSM_APPLECSPDL_DB_CHANGE_PASSWORD:
		{
			if (!inInputParams)
				CssmError::throwMe(CSSM_ERRCODE_INVALID_INPUT_POINTER);

			const CSSM_APPLECSPDL_DB_CHANGE_PASSWORD_PARAMETERS *params =
				reinterpret_cast<const CSSM_APPLECSPDL_DB_CHANGE_PASSWORD_PARAMETERS *>(inInputParams);
			db->changePassphrase(params->accessCredentials);
			break;
		}
		case CSSM_APPLECSPDL_DB_GET_HANDLE:
		{
			using SecurityServer::DbHandle;
			Required(outOutputParams, CSSM_ERRCODE_INVALID_OUTPUT_POINTER);
			*reinterpret_cast<CSSM_DL_DB_HANDLE *>(outOutputParams) = db->handle();
			break;
		}
		case CSSM_APPLECSPDL_CSP_RECODE:
		{
			if (!inInputParams)
				CssmError::throwMe(CSSM_ERRCODE_INVALID_INPUT_POINTER);

			const CSSM_APPLECSPDL_RECODE_PARAMETERS *params =
				reinterpret_cast<const CSSM_APPLECSPDL_RECODE_PARAMETERS *>(inInputParams);
			
			db->recode(CssmData::overlay(params->dbBlob),
				CssmData::overlay(params->extraData));
			break;
		}
		case CSSM_APPLECSPDL_DB_GET_RECORD_IDENTIFIER:
		{
			SSUniqueRecord uniqueID = findSSUniqueRecord(*(CSSM_DB_UNIQUE_RECORD_PTR) inInputParams);
			db->getRecordIdentifier(uniqueID, *reinterpret_cast<CSSM_DATA *>(outOutputParams));
			break;
		}
		case CSSM_APPLECSPDL_DB_COPY_BLOB:
		{
			// make the output parameters
			db->copyBlob(*reinterpret_cast<CSSM_DATA *>(outOutputParams));
			break;
		}
		case CSSM_APPLECSPDL_DB_INSERT_WITHOUT_ENCRYPTION:
		{
			doInsertWithoutEncryption (db, inInputParams, outOutputParams);
			break;
		}
		case CSSM_APPLECSPDL_DB_MODIFY_WITHOUT_ENCRYPTION:
		{
			doModifyWithoutEncryption (db, inInputParams, outOutputParams);
			break;
		}
		case CSSM_APPLECSPDL_DB_GET_WITHOUT_ENCRYPTION:
		{
			doGetWithoutEncryption (db, inInputParams, outOutputParams);
			break;
		}
		case CSSM_APPLECSPDL_DB_CONVERT_RECORD_IDENTIFIER:
		{
			doConvertRecordIdentifier (db, inInputParams, outOutputParams);
			break;
		}
		default:
		{
			CSSM_RETURN result = CSSM_DL_PassThrough(db->handle(), inPassThroughId, inInputParams, outOutputParams);
			if (result)
				CssmError::throwMe(result);
			break;
		}
	}
}

CSSM_DB_HANDLE
SSDLSession::makeDbHandle(SSDatabase &inDb)
{
	StLock<Mutex> _(mDbHandleLock);
	CSSM_DB_HANDLE aDbHandle = inDb->handle().DBHandle;
	IFDEBUG(bool inserted =) mDbHandleMap.insert(DbHandleMap::value_type(aDbHandle, inDb)).second;
	assert(inserted);
	return aDbHandle;
}

SSDatabase
SSDLSession::killDbHandle(CSSM_DB_HANDLE inDbHandle)
{
	StLock<Mutex> _(mDbHandleLock);
	DbHandleMap::iterator it = mDbHandleMap.find(inDbHandle);
	if (it == mDbHandleMap.end())
		CssmError::throwMe(CSSMERR_DL_INVALID_DB_HANDLE);

	SSDatabase db = it->second;
	mDbHandleMap.erase(it);
	return db;
}

SSDatabase
SSDLSession::findDbHandle(CSSM_DB_HANDLE inDbHandle)
{
	StLock<Mutex> _(mDbHandleLock);
	DbHandleMap::iterator it = mDbHandleMap.find(inDbHandle);
	if (it == mDbHandleMap.end())
		CssmError::throwMe(CSSMERR_DL_INVALID_DB_HANDLE);

	return it->second;
}

CSSM_DB_UNIQUE_RECORD_PTR
SSDLSession::makeSSUniqueRecord(SSUniqueRecord &uniqueId)
{
	StLock<Mutex> _(mSSUniqueRecordLock);
	CSSM_HANDLE ref = CSSM_HANDLE(static_cast<CSSM_DB_UNIQUE_RECORD *>(uniqueId));
	IFDEBUG(bool inserted =) mSSUniqueRecordMap.insert(SSUniqueRecordMap::value_type(ref, uniqueId)).second;
	assert(inserted);
	return createUniqueRecord(ref);
}

SSUniqueRecord
SSDLSession::killSSUniqueRecord(CSSM_DB_UNIQUE_RECORD &inUniqueRecord)
{
	CSSM_HANDLE ref = parseUniqueRecord(inUniqueRecord);
	StLock<Mutex> _(mSSUniqueRecordLock);
	SSUniqueRecordMap::iterator it = mSSUniqueRecordMap.find(ref);
	if (it == mSSUniqueRecordMap.end())
		CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID);

	SSUniqueRecord uniqueRecord = it->second;
	mSSUniqueRecordMap.erase(it);
	freeUniqueRecord(inUniqueRecord);
	return uniqueRecord;
}

SSUniqueRecord
SSDLSession::findSSUniqueRecord(const CSSM_DB_UNIQUE_RECORD &inUniqueRecord)
{
	CSSM_HANDLE ref = parseUniqueRecord(inUniqueRecord);
	StLock<Mutex> _(mSSUniqueRecordLock);
	SSUniqueRecordMap::iterator it = mSSUniqueRecordMap.find(ref);
	if (it == mSSUniqueRecordMap.end())
		CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID);

	return it->second;
}

CSSM_DB_UNIQUE_RECORD_PTR
SSDLSession::createUniqueRecord(CSSM_HANDLE ref)
{
	CSSM_DB_UNIQUE_RECORD *aUniqueRecord = allocator().alloc<CSSM_DB_UNIQUE_RECORD>();
	memset(aUniqueRecord, 0, sizeof(CSSM_DB_UNIQUE_RECORD));
	aUniqueRecord->RecordIdentifier.Length = sizeof(CSSM_HANDLE);
	try
	{
		aUniqueRecord->RecordIdentifier.Data = allocator().alloc<uint8>(sizeof(CSSM_HANDLE));
		*reinterpret_cast<CSSM_HANDLE *>(aUniqueRecord->RecordIdentifier.Data) = ref;
	}
	catch(...)
	{
		free(aUniqueRecord);
		throw;
	}

	return aUniqueRecord;
}

CSSM_HANDLE
SSDLSession::parseUniqueRecord(const CSSM_DB_UNIQUE_RECORD &inUniqueRecord)
{
	if (inUniqueRecord.RecordIdentifier.Length != sizeof(CSSM_HANDLE))
		CssmError::throwMe(CSSMERR_DL_INVALID_RECORD_UID);

	return *reinterpret_cast<CSSM_HANDLE *>(inUniqueRecord.RecordIdentifier.Data);
}

void
SSDLSession::freeUniqueRecord(CSSM_DB_UNIQUE_RECORD &inUniqueRecord)
{
	if (inUniqueRecord.RecordIdentifier.Length != 0
		&& inUniqueRecord.RecordIdentifier.Data != NULL)
	{
		inUniqueRecord.RecordIdentifier.Length = 0;
		allocator().free(inUniqueRecord.RecordIdentifier.Data);
	}
	allocator().free(&inUniqueRecord);
}
