#include "KCCursor.h"
#include "Item.h"
#include <security_cdsa_utilities/Schema.h>
#include <security_cdsa_utilities/KeySchema.h>
#include "cssmdatetime.h"
#include "Globals.h"
#include "StorageManager.h"
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include <Security/SecKeychainItemPriv.h>
using namespace KeychainCore;
using namespace CssmClient;
using namespace CSSMDateTimeUtils;
using namespace KeySchema;
static const CSSM_DB_ATTRIBUTE_INFO* gKeyAttributeLookupTable[] =
{
&KeyClass, &PrintName, &Alias, &Permanent, &Private, &Modifiable, &Label, &ApplicationTag, &KeyCreator,
&KeyType, &KeySizeInBits, &EffectiveKeySize, &StartDate, &EndDate, &Sensitive, &AlwaysSensitive, &Extractable,
&NeverExtractable, &Encrypt, &Decrypt, &Derive, &Sign, &Verify, &SignRecover, &VerifyRecover, &Wrap, &Unwrap
};
KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList &searchList, SecItemClass itemClass, const SecKeychainAttributeList *attrList, CSSM_DB_CONJUNCTIVE dbConjunctive, CSSM_DB_OPERATOR dbOperator) :
mSearchList(searchList),
mCurrent(mSearchList.begin()),
mAllFailed(true),
mMutex(Mutex::recursive)
{
recordType(Schema::recordTypeFor(itemClass));
if (!attrList) return;
conjunctive(dbConjunctive);
const SecKeychainAttribute *end=&attrList->attr[attrList->count];
for (const SecKeychainAttribute *attr=attrList->attr; attr != end; ++attr)
{
const CSSM_DB_ATTRIBUTE_INFO *temp;
if (attr->tag <' ') {
temp = gKeyAttributeLookupTable[attr->tag];
}
else
{
temp = &Schema::attributeInfo(attr->tag);
}
const CssmDbAttributeInfo &info = *temp;
void *buf = attr->data;
UInt32 length = attr->length;
uint8 timeString[16];
if (info.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
{
if (length == sizeof(UInt32))
{
MacSecondsToTimeString(*reinterpret_cast<const UInt32 *>(buf),
16, &timeString);
buf = &timeString;
length = 16;
}
else if (length == sizeof(SInt64))
{
MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 *>(buf),
16, &timeString);
buf = &timeString;
length = 16;
}
}
add(dbOperator ,info, CssmData(buf,length));
}
}
KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList &searchList, const SecKeychainAttributeList *attrList) :
mSearchList(searchList),
mCurrent(mSearchList.begin()),
mAllFailed(true),
mMutex(Mutex::recursive)
{
if (!attrList) return;
conjunctive(CSSM_DB_AND);
bool foundClassAttribute=false;
const SecKeychainAttribute *end=&attrList->attr[attrList->count];
for (const SecKeychainAttribute *attr=attrList->attr; attr != end; ++attr)
{
if (attr->tag!=kSecClassItemAttr) {
const CssmDbAttributeInfo &info = Schema::attributeInfo(attr->tag);
void *buf = attr->data;
UInt32 length = attr->length;
uint8 timeString[16];
if (info.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
{
if (length == sizeof(UInt32))
{
MacSecondsToTimeString(*reinterpret_cast<const UInt32 *>(buf),
16, &timeString);
buf = &timeString;
length = 16;
}
else if (length == sizeof(SInt64))
{
MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 *>(buf),
16, &timeString);
buf = &timeString;
length = 16;
}
}
add(CSSM_DB_EQUAL,info, CssmData(buf,length));
continue;
}
if (foundClassAttribute || attr->length != sizeof(SecItemClass))
MacOSError::throwMe(paramErr);
recordType(Schema::recordTypeFor(*reinterpret_cast<SecItemClass *>(attr->data)));
foundClassAttribute=true;
}
}
KCCursorImpl::~KCCursorImpl() throw()
{
}
static ModuleNexus<Mutex> gActivationMutex;
bool
KCCursorImpl::next(Item &item)
{
StLock<Mutex>_(mMutex);
DbAttributes dbAttributes;
DbUniqueRecord uniqueId;
OSStatus status = 0;
for (;;)
{
while (!mDbCursor)
{
if (mCurrent == mSearchList.end())
{
if (mAllFailed && status)
CssmError::throwMe(status);
return false;
}
try
{
StLock<Mutex> _(gActivationMutex()); (*mCurrent)->database()->activate();
mDbCursor = DbCursor((*mCurrent)->database(), *this);
}
catch(const CommonError &err)
{
++mCurrent;
}
}
bool gotRecord;
try
{
dbAttributes.clear();
gotRecord = mDbCursor->next(&dbAttributes, NULL, uniqueId);
mAllFailed = false;
}
catch(const CommonError &err)
{
status = err.osStatus();
gotRecord = false;
dbAttributes.invalidate();
}
catch(...)
{
status = errSecItemNotFound;
gotRecord = false;
}
if (!gotRecord)
{
++mCurrent;
mDbCursor = DbCursor();
continue;
}
if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_METADATA &&
mDbCursor->recordType() == CSSM_DL_DB_RECORD_ANY)
continue;
if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
{
bool groupKey = false;
try
{
dbAttributes.add(KeySchema::Label);
Db db((*mCurrent)->database());
CSSM_RETURN getattr_result = CSSM_DL_DataGetFromUniqueRecordId(db->handle(), uniqueId, &dbAttributes, NULL);
if (getattr_result == CSSM_OK)
{
CssmDbAttributeData *label = dbAttributes.find(KeySchema::Label);
CssmData attrData;
if (label)
attrData = *label;
if (attrData.length() > 4 && !memcmp(attrData.data(), "ssgp", 4))
groupKey = true;
}
else
{
dbAttributes.invalidate();
}
}
catch (...) {}
if (groupKey)
continue;
}
break;
}
item = (*mCurrent)->item(dbAttributes.recordType(), uniqueId);
return true;
}