#include "Item.h"
#include "Certificate.h"
#include "KeyItem.h"
#include "ExtendedAttribute.h"
#include "Globals.h"
#include <security_cdsa_utilities/Schema.h>
#include "KCEventNotifier.h"
#include "KCExceptions.h"
#include "cssmdatetime.h"
#include <security_cdsa_client/keychainacl.h>
#include <security_utilities/osxcode.h>
#include <security_utilities/trackingallocator.h>
#include <Security/SecKeychainItemPriv.h>
#include <Security/cssmapple.h>
#include <CommonCrypto/CommonDigest.h>
#include <utilities/der_plist.h>
#include <security_utilities/CSPDLTransaction.h>
#include <Security/SecBasePriv.h>
#define _kSecAppleSharePasswordItemClass 'ashp'
#define APPLEDB_CSSM_PRINTNAME_ATTRIBUTE 1
#define APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE 7
#define IS_PASSWORD_ITEM_CLASS(X) ( (X) == kSecInternetPasswordItemClass || \
(X) == kSecGenericPasswordItemClass || \
(X) == _kSecAppleSharePasswordItemClass ) ? 1 : 0
using namespace KeychainCore;
using namespace CSSMDateTimeUtils;
ItemImpl *ItemImpl::required(SecKeychainItemRef ptr)
{
if (ptr != NULL) {
if (ItemImpl *pp = optional(ptr)) {
return pp;
}
}
MacOSError::throwMe(errSecInvalidItemRef);
}
ItemImpl *ItemImpl::optional(SecKeychainItemRef ptr)
{
if (ptr != NULL && CFGetTypeID(ptr) == SecKeyGetTypeID()) {
return dynamic_cast<ItemImpl *>(KeyItem::fromSecKeyRef(ptr));
} else if (SecCFObject *p = SecCFObject::optional(ptr)) {
if (ItemImpl *pp = dynamic_cast<ItemImpl *>(p)) {
return pp;
} else {
MacOSError::throwMe(errSecInvalidItemRef);
}
} else {
return NULL;
}
}
ItemImpl::ItemImpl(SecItemClass itemClass, OSType itemCreator, UInt32 length, const void* data, bool dontDoAttributes)
: mDbAttributes(new DbAttributes()),
mKeychain(NULL),
secd_PersistentRef(NULL),
mDoNotEncrypt(false),
mInCache(false),
mMutex(Mutex::recursive)
{
if (length && data)
mData = new CssmDataContainer(data, length);
mDbAttributes->recordType(Schema::recordTypeFor(itemClass));
if (itemCreator)
mDbAttributes->add(Schema::attributeInfo(kSecCreatorItemAttr), itemCreator);
}
ItemImpl::ItemImpl(SecItemClass itemClass, SecKeychainAttributeList *attrList, UInt32 length, const void* data)
: mDbAttributes(new DbAttributes()),
mKeychain(NULL),
secd_PersistentRef(NULL),
mDoNotEncrypt(false),
mInCache(false),
mMutex(Mutex::recursive)
{
if (length && data)
mData = new CssmDataContainer(data, length);
mDbAttributes->recordType(Schema::recordTypeFor(itemClass));
if(attrList)
{
for(UInt32 i=0; i < attrList->count; i++)
{
mDbAttributes->add(Schema::attributeInfo(attrList->attr[i].tag), CssmData(attrList->attr[i].data, attrList->attr[i].length));
}
}
}
ItemImpl::ItemImpl(const Keychain &keychain, const PrimaryKey &primaryKey, const DbUniqueRecord &uniqueId)
: mUniqueId(uniqueId), mKeychain(keychain), mPrimaryKey(primaryKey),
secd_PersistentRef(NULL), mDoNotEncrypt(false), mInCache(false),
mMutex(Mutex::recursive)
{
}
ItemImpl::ItemImpl(const Keychain &keychain, const PrimaryKey &primaryKey)
: mKeychain(keychain), mPrimaryKey(primaryKey), secd_PersistentRef(NULL), mDoNotEncrypt(false),
mInCache(false),
mMutex(Mutex::recursive)
{
}
ItemImpl* ItemImpl::make(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId)
{
ItemImpl* ii = new ItemImpl(keychain, primaryKey, uniqueId);
keychain->addItem(primaryKey, ii);
return ii;
}
ItemImpl* ItemImpl::make(const Keychain &keychain, const PrimaryKey &primaryKey)
{
ItemImpl* ii = new ItemImpl(keychain, primaryKey);
keychain->addItem(primaryKey, ii);
return ii;
}
ItemImpl::ItemImpl(ItemImpl &item) :
mData(item.modifiedData() ? NULL : new CssmDataContainer()),
mDbAttributes(new DbAttributes()),
mKeychain(NULL),
secd_PersistentRef(NULL),
mDoNotEncrypt(false),
mInCache(false),
mMutex(Mutex::recursive)
{
mDbAttributes->recordType(item.recordType());
if (item.mKeychain) {
item.fillDbAttributesFromSchema(*mDbAttributes, item.recordType());
item.getContent(mDbAttributes.get(), mData.get());
}
if (item.modifiedData())
mData = new CssmDataContainer(item.modifiedData()->Data,
item.modifiedData()->Length);
}
ItemImpl::~ItemImpl()
try {
if (secd_PersistentRef) {
CFRelease(secd_PersistentRef);
}
} catch (...) {
#ifndef NDEBUG
#else
return;
#endif
}
Mutex*
ItemImpl::getMutexForObject() const
{
if (mKeychain.get())
{
return mKeychain->getKeychainMutex();
}
return NULL;
}
void
ItemImpl::aboutToDestruct()
{
if(mKeychain.get()) {
mKeychain->forceRemoveFromCache(this);
}
}
void
ItemImpl::didModify()
{
StLock<Mutex>_(mMutex);
mData = NULL;
mDbAttributes.reset(NULL);
}
const CSSM_DATA &
ItemImpl::defaultAttributeValue(const CSSM_DB_ATTRIBUTE_INFO &info)
{
static const uint32 zeroInt = 0;
static const double zeroDouble = 0.0;
static const char timeBytes[] = "20010101000000Z";
static const CSSM_DATA defaultFourBytes = { 4, (uint8 *) &zeroInt };
static const CSSM_DATA defaultEightBytes = { 8, (uint8 *) &zeroDouble };
static const CSSM_DATA defaultTime = { 16, (uint8 *) timeBytes };
static const CSSM_DATA defaultZeroBytes = { 0, NULL };
switch (info.AttributeFormat)
{
case CSSM_DB_ATTRIBUTE_FORMAT_SINT32:
case CSSM_DB_ATTRIBUTE_FORMAT_UINT32:
return defaultFourBytes;
case CSSM_DB_ATTRIBUTE_FORMAT_REAL:
return defaultEightBytes;
case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE:
return defaultTime;
default:
return defaultZeroBytes;
}
}
void ItemImpl::fillDbAttributesFromSchema(DbAttributes& dbAttributes, CSSM_DB_RECORDTYPE recordType, Keychain keychain) {
keychain = !!keychain ? keychain : mKeychain;
if(!keychain) {
return;
}
SecKeychainAttributeInfo* infos;
keychain->getAttributeInfoForItemID(recordType, &infos);
secinfo("integrity", "filling %u attributes for type %u", (unsigned int)infos->count, recordType);
for (uint32 i = 0; i < infos->count; i++) {
CSSM_DB_ATTRIBUTE_INFO info;
memset(&info, 0, sizeof(info));
info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
info.Label.AttributeID = infos->tag[i];
info.AttributeFormat = infos->format[i];
dbAttributes.add(info);
}
keychain->freeAttributeInfo(infos);
}
DbAttributes* ItemImpl::getCurrentAttributes() {
DbAttributes* dbAttributes;
secinfo("integrity", "getting current attributes...");
if(mUniqueId.get()) {
dbAttributes = new DbAttributes(dbUniqueRecord()->database(), 1);
fillDbAttributesFromSchema(*dbAttributes, recordType());
mUniqueId->get(dbAttributes, NULL);
if(mDbAttributes.get()) {
secinfo("integrity", "adding %d attributes from mDbAttributes", mDbAttributes->size());
dbAttributes->updateWithDbAttributes(&(*mDbAttributes.get()));
}
} else if (mDbAttributes.get()) {
secnotice("integrity", "no unique id, using %d attributes from mDbAttributes", mDbAttributes->size());
dbAttributes = new DbAttributes();
dbAttributes->updateWithDbAttributes(&(*mDbAttributes.get()));
} else {
secnotice("integrity", "no attributes at all");
dbAttributes = new DbAttributes();
}
dbAttributes->recordType(recordType());
return dbAttributes;
}
void ItemImpl::encodeAttributes(CssmOwnedData &attributeBlob) {
unique_ptr<DbAttributes> dbAttributes(getCurrentAttributes());
encodeAttributesFromDictionary(attributeBlob, dbAttributes.get());
}
void ItemImpl::encodeAttributesFromDictionary(CssmOwnedData &attributeBlob, DbAttributes* dbAttributes) {
CFRef<CFMutableDictionaryRef> attributes;
attributes.take(CFDictionaryCreateMutable(NULL, dbAttributes->size(), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
secinfo("integrity", "looking at %d attributes", dbAttributes->size());
for(int i = 0; i < dbAttributes->size(); i++) {
CssmDbAttributeData& data = dbAttributes->attributes()[i];
CssmDbAttributeInfo& datainfo = data.info();
CssmDbAttributeInfo& actualInfo = datainfo;
try {
if(datainfo.nameFormat() == CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER && Schema::haveAttributeInfo(datainfo.intName())) {
actualInfo = Schema::attributeInfo(datainfo.intName());
}
} catch(...) {
actualInfo = datainfo;
}
CFRef<CFDataRef> label = NULL;
switch(actualInfo.nameFormat()) {
case CSSM_DB_ATTRIBUTE_NAME_AS_STRING: {
const char* stringname = actualInfo.stringName();
label.take(CFDataCreate(NULL, reinterpret_cast<const UInt8*>(stringname), strlen(stringname)));
break;
}
case CSSM_DB_ATTRIBUTE_NAME_AS_OID: {
const CssmOid& oidname = actualInfo.oidName();
label.take(CFDataCreate(NULL, reinterpret_cast<const UInt8*>(oidname.data()), oidname.length()));
break;
}
case CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER: {
uint32 iname = actualInfo.intName();
label.take(CFDataCreate(NULL, reinterpret_cast<const UInt8*>(&(iname)), sizeof(uint32)));
break;
}
}
if(data.size() == 0) {
continue;
}
SecKeychainAttrType cdat = kSecCreationDateItemAttr;
SecKeychainAttrType cmod = kSecModDateItemAttr;
if((CFDataGetLength(label) == sizeof(SecKeychainAttrType)) &&
((memcmp(CFDataGetBytePtr(label), &cdat, sizeof(SecKeychainAttrType)) == 0) ||
(memcmp(CFDataGetBytePtr(label), &cmod, sizeof(SecKeychainAttrType)) == 0))) {
continue;
}
CFRef<CFMutableArrayRef> attributeDataContainer;
attributeDataContainer.take(CFArrayCreateMutable(NULL, data.size(), &kCFTypeArrayCallBacks));
for(int j = 0; j < data.size(); j++) {
CssmData& entry = data.values()[j];
CFRef<CFDataRef> datadata = NULL;
switch(actualInfo.format()) {
case CSSM_DB_ATTRIBUTE_FORMAT_BLOB:
case CSSM_DB_ATTRIBUTE_FORMAT_STRING:
case CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE:
datadata.take(CFDataCreate(NULL, reinterpret_cast<const UInt8*>(data.values()[j].data()), data.values()[j].length()));
break;
case CSSM_DB_ATTRIBUTE_FORMAT_UINT32: {
uint32 x = entry.length() == 1 ? *reinterpret_cast<uint8 *>(entry.Data) :
entry.length() == 2 ? *reinterpret_cast<uint16 *>(entry.Data) :
entry.length() == 4 ? *reinterpret_cast<uint32 *>(entry.Data) : 0;
datadata.take(CFDataCreate(NULL, reinterpret_cast<const UInt8*>(&x), sizeof(x)));
break;
}
case CSSM_DB_ATTRIBUTE_FORMAT_SINT32: {
sint32 x = entry.length() == 1 ? *reinterpret_cast<sint8 *>(entry.Data) :
entry.length() == 2 ? *reinterpret_cast<sint16 *>(entry.Data) :
entry.length() == 4 ? *reinterpret_cast<sint32 *>(entry.Data) : 0;
datadata.take(CFDataCreate(NULL, reinterpret_cast<const UInt8*>(&x), sizeof(x)));
break;
}
default:
continue;
}
CFArrayAppendValue(attributeDataContainer, datadata);
}
CFDictionaryAddValue(attributes, label, attributeDataContainer);
}
CFErrorRef error;
CFRef<CFDataRef> derBlob;
derBlob.take(CFPropertyListCreateDERData(NULL, attributes, &error));
if(!derBlob) {
return;
}
attributeBlob.length(CFDataGetLength(derBlob));
attributeBlob.copy(CFDataGetBytePtr(derBlob), CFDataGetLength(derBlob));
}
void ItemImpl::computeDigest(CssmOwnedData &sha2) {
unique_ptr<DbAttributes> dbAttributes(getCurrentAttributes());
ItemImpl::computeDigestFromDictionary(sha2, dbAttributes.get());
}
void ItemImpl::computeDigestFromDictionary(CssmOwnedData &sha2, DbAttributes* dbAttributes) {
try{
CssmAutoData attributeBlob(Allocator::standard());
encodeAttributesFromDictionary(attributeBlob, dbAttributes);
sha2.length(CC_SHA256_DIGEST_LENGTH);
CC_SHA256(attributeBlob.get().data(), static_cast<CC_LONG>(attributeBlob.get().length()), sha2);
secinfo("integrity", "finished: %s", sha2.get().toHex().c_str());
} catch (MacOSError mose) {
secnotice("integrity", "MacOSError: %d", (int)mose.osStatus());
} catch (...) {
secnotice("integrity", "unknown exception");
}
}
void ItemImpl::addIntegrity(Access &access, bool force) {
if(!force && (!mKeychain || !mKeychain->hasIntegrityProtection())) {
secinfo("integrity", "skipping integrity add due to keychain version\n");
return;
}
ACL * acl = NULL;
CssmAutoData digest(Allocator::standard());
computeDigest(digest);
vector<ACL *> acls;
access.findSpecificAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY, acls);
if(acls.size() >= 1) {
acl = acls[0];
secinfo("integrity", "previous integrity acl exists; setting integrity");
acl->setIntegrity(digest.get());
for(int i = 1; i < acls.size(); i++) {
secnotice("integrity", "extra integrity acls exist; removing %d",i);
acls[i]->remove();
}
} else if(acls.size() == 0) {
secnotice("integrity", "no previous integrity acl exists; making a new one");
acl = new ACL(digest.get());
access.add(acl);
}
}
void ItemImpl::setIntegrity(bool force) {
if(!force && (!mKeychain || !mKeychain->hasIntegrityProtection())) {
secnotice("integrity", "skipping integrity set due to keychain version");
return;
}
if(!(recordType() == CSSM_DL_DB_RECORD_GENERIC_PASSWORD || recordType() == CSSM_DL_DB_RECORD_INTERNET_PASSWORD)) {
return;
}
Db db(mKeychain->database());
if (!useSecureStorage(db)) {
return;
}
setIntegrity(*group(), force);
}
void ItemImpl::setIntegrity(AclBearer &bearer, bool force) {
if(!force && (!mKeychain || !mKeychain->hasIntegrityProtection())) {
secnotice("integrity", "skipping integrity acl set due to keychain version");
return;
}
SecPointer<Access> access = new Access(bearer);
access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID);
addIntegrity(*access, force);
access->setAccess(bearer, true);
}
void ItemImpl::removeIntegrity(const AccessCredentials *cred) {
removeIntegrity(*group(), cred);
}
void ItemImpl::removeIntegrity(AclBearer &bearer, const AccessCredentials *cred) {
SecPointer<Access> access = new Access(bearer);
vector<ACL *> acls;
access->findSpecificAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY, acls);
for(int i = 0; i < acls.size(); i++) {
acls[i]->remove();
}
access->findSpecificAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID, acls);
for(int i = 0; i < acls.size(); i++) {
acls[i]->remove();
}
access->editAccess(bearer, true, cred);
}
bool ItemImpl::checkIntegrity() {
if(!isPersistent()) {
secnotice("integrity", "no keychain, integrity is valid?");
return true;
}
if(!mKeychain || !mKeychain->hasIntegrityProtection()) {
secinfo("integrity", "skipping integrity check due to keychain version");
return true;
}
dbUniqueRecord();
SSGroup ssGroup = group();
if(ssGroup) {
return checkIntegrity(*ssGroup);
}
return true;
}
bool ItemImpl::checkIntegrity(AclBearer& aclBearer) {
if(!mKeychain || !mKeychain->hasIntegrityProtection()) {
secinfo("integrity", "skipping integrity check due to keychain version");
return true;
}
unique_ptr<DbAttributes> dbAttributes(getCurrentAttributes());
return checkIntegrityFromDictionary(aclBearer, dbAttributes.get());
}
bool ItemImpl::checkIntegrityFromDictionary(AclBearer& aclBearer, DbAttributes* dbAttributes) {
try {
AutoAclEntryInfoList aclInfos;
aclBearer.getAcl(aclInfos, CSSM_APPLE_ACL_TAG_INTEGRITY);
AclEntryInfo &info = aclInfos.at(0);
unique_ptr<ACL> acl(new ACL(info, Allocator::standard()));
for(int i = 1; i < aclInfos.count(); i++) {
secnotice("integrity", "*** DUPLICATE INTEGRITY ACL, something has gone wrong");
}
CssmAutoData digest(Allocator::standard());
computeDigestFromDictionary(digest, dbAttributes);
if (acl->integrity() == digest.get()) {
return true;
}
}
catch (CssmError cssme) {
const char* errStr = cssmErrorString(cssme.error);
secnotice("integrity", "caught CssmError: %d %s", (int) cssme.error, errStr);
if(cssme.error == CSSMERR_CSP_ACL_ENTRY_TAG_NOT_FOUND) {
return true;
}
if(cssme.error == CSSMERR_CSP_INVALID_ACL_SUBJECT_VALUE) {
secnotice("integrity", "INVALID ITEM (too many integrity acls)");
return false;
}
if(cssme.error == CSSMERR_CSP_VERIFY_FAILED) {
secnotice("integrity", "MAC verification failed; something has gone very wrong");
return false; }
throw;
}
secnotice("integrity", "***** INVALID ITEM");
return false;
}
PrimaryKey ItemImpl::addWithCopyInfo (Keychain &keychain, bool isCopy)
{
StLock<Mutex>_(mMutex);
if (mKeychain)
MacOSError::throwMe(errSecDuplicateItem);
if (!mDbAttributes.get())
MacOSError::throwMe(errSecDuplicateItem);
CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType();
if (!isCopy)
{
KeychainSchema schema = keychain->keychainSchema();
SInt64 date;
GetCurrentMacLongDateTime(date);
if (schema->hasAttribute(recordType, kSecCreationDateItemAttr))
{
setAttribute(schema->attributeInfoFor(recordType, kSecCreationDateItemAttr), date);
}
if (schema->hasAttribute(recordType, kSecModDateItemAttr))
{
setAttribute(schema->attributeInfoFor(recordType, kSecModDateItemAttr), date);
}
}
mDbAttributes->canonicalize(); if (!mDoNotEncrypt && !mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr)))
{
CssmDbAttributeData *label = NULL;
switch (recordType)
{
case CSSM_DL_DB_RECORD_GENERIC_PASSWORD:
label = mDbAttributes->find(Schema::attributeInfo(kSecServiceItemAttr));
break;
case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD:
case CSSM_DL_DB_RECORD_INTERNET_PASSWORD:
label = mDbAttributes->find(Schema::attributeInfo(kSecServerItemAttr));
if (!label) label = mDbAttributes->find(Schema::attributeInfo(kSecAddressItemAttr));
break;
default:
break;
}
if (!label)
label = mDbAttributes->find(Schema::attributeInfo(kSecAccountItemAttr));
if (label && label->size())
setAttribute (Schema::attributeInfo(kSecLabelItemAttr), label->at<CssmData>(0));
}
const CssmAutoDbRecordAttributeInfo &primaryKeyInfos =
keychain->primaryKeyInfosFor(recordType);
DbAttributes *attributes = mDbAttributes.get();
typedef set<CssmDbAttributeInfo> InfoSet;
InfoSet infoSet;
if (!mDoNotEncrypt)
{
for (uint32 i = 0; i < attributes->size(); i++)
infoSet.insert(attributes->at(i).Info);
for (uint32 i = 0; i < primaryKeyInfos.size(); i++) { InfoSet::const_iterator it = infoSet.find(primaryKeyInfos.at(i));
if (it == infoSet.end()) { attributes->add(primaryKeyInfos.at(i), defaultAttributeValue(primaryKeyInfos.at(i)));
}
}
}
try {
mKeychain = keychain;
StLock<Mutex>_(*(mKeychain->getKeychainMutex()));
Db db(keychain->database());
if (mDoNotEncrypt)
{
mUniqueId = db->insertWithoutEncryption (recordType, NULL, mData.get());
}
else if (useSecureStorage(db))
{
updateSSGroup(db, recordType, mData.get(), keychain, mAccess);
mAccess = NULL; }
else
{
mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
}
mPrimaryKey = keychain->makePrimaryKey(recordType, mUniqueId);
} catch(...) {
mKeychain = NULL;
throw;
}
mData = NULL;
mDbAttributes.reset(NULL);
return mPrimaryKey;
}
PrimaryKey
ItemImpl::add (Keychain &keychain)
{
return addWithCopyInfo (keychain, false);
}
Item
ItemImpl::copyTo(const Keychain &keychain, Access *newAccess)
{
StLock<Mutex>_(mMutex);
Item item(*this);
if (newAccess) {
newAccess->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID);
newAccess->removeAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY);
item->setAccess(newAccess);
} else {
SSGroup myGroup = group();
if (myGroup)
{
SecPointer<Access> access = new Access(*myGroup);
access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID);
access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY);
item->setAccess(access);
}
}
keychain->addCopy(item);
return item;
}
void
ItemImpl::update()
{
StLock<Mutex>_(mMutex);
if (!mKeychain)
MacOSError::throwMe(errSecNoSuchKeychain);
if (!isModified())
return;
StLock<Mutex>__(*(mKeychain->getKeychainMutex()));
CSSM_DB_RECORDTYPE aRecordType = recordType();
KeychainSchema schema = mKeychain->keychainSchema();
if (schema->hasAttribute(aRecordType, kSecModDateItemAttr))
{
SInt64 date;
GetCurrentMacLongDateTime(date);
setAttribute(schema->attributeInfoFor(aRecordType, kSecModDateItemAttr), date);
}
Db db(dbUniqueRecord()->database());
if (mDoNotEncrypt)
{
CSSM_DB_RECORD_ATTRIBUTE_DATA attrData;
memset (&attrData, 0, sizeof (attrData));
attrData.DataRecordType = aRecordType;
dbUniqueRecord()->modifyWithoutEncryption(aRecordType,
&attrData,
mData.get(),
CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
}
else if (useSecureStorage(db))
{
updateSSGroup(db, recordType(), mData.get());
}
else
{
dbUniqueRecord()->modify(aRecordType,
mDbAttributes.get(),
mData.get(),
CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
}
if (!mDoNotEncrypt)
{
PrimaryKey oldPK = mPrimaryKey;
mPrimaryKey = mKeychain->makePrimaryKey(aRecordType, mUniqueId);
mData = NULL;
mDbAttributes.reset(NULL);
mKeychain->didUpdate(this, oldPK, mPrimaryKey);
}
}
void
ItemImpl::updateSSGroup(Db& db, CSSM_DB_RECORDTYPE recordType, CssmDataContainer* newdata, Keychain keychain, SecPointer<Access> access)
{
AclFactory aclFactory;
const AccessCredentials *nullCred = aclFactory.nullCred();
bool haveOldUniqueId = !!mUniqueId.get();
SSDbUniqueRecord ssUniqueId(NULL);
SSGroup ssGroup(NULL);
if(haveOldUniqueId) {
ssUniqueId = SSDbUniqueRecord(dynamic_cast<SSDbUniqueRecordImpl *>(&(*mUniqueId)));
if (ssUniqueId.get() == NULL) {
CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
}
ssGroup = ssUniqueId->group();
}
bool saveToNewSSGroup = (!!newdata) || (!haveOldUniqueId);
if (!mDbAttributes.get())
{
secinfo("integrity", "making new dbattributes");
mDbAttributes.reset(new DbAttributes());
mDbAttributes->recordType(mPrimaryKey->recordType());
}
SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*db));
if (impl == NULL)
{
CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
}
SSDb ssDb(impl);
TrackingAllocator allocator(Allocator::standard());
if ((!access) && (haveOldUniqueId)) {
secinfo("integrity", "copying old ACL");
access = new Access(*(ssGroup));
access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID);
access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY);
} else if (!access) {
secinfo("integrity", "setting up new ACL");
CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr));
string printName = data ? CssmData::overlay(data->Value[0]).toString() : "keychain item";
access = new Access(printName);
} else {
secinfo("integrity", "passed an Access, use it");
}
if(haveOldUniqueId) {
mDbAttributes.reset(getCurrentAttributes());
}
CSPDLTransaction transaction(db);
Access::Maker maker;
ResourceControlContext prototype;
maker.initialOwner(prototype, nullCred);
if(saveToNewSSGroup) {
secinfo("integrity", "saving to a new SSGroup");
if(haveOldUniqueId) {
secinfo("integrity", "deleting old mUniqueId");
mUniqueId->deleteRecord();
mUniqueId.release();
} else {
secinfo("integrity", "no old mUniqueId");
}
SSGroup newSSGroup(ssDb, &prototype);
const AccessCredentials * cred = maker.cred();
try {
doChange(keychain, recordType, ^{
mUniqueId = ssDb->ssInsert(recordType, mDbAttributes.get(), newdata, newSSGroup, cred);
});
addIntegrity(*access);
access->setAccess(*newSSGroup, maker);
mDbAttributes.reset(NULL);
transaction.commit();
}
catch (CssmError cssme) {
const char* errStr = cssmErrorString(cssme.error);
secnotice("integrity", "caught CssmError during add: %d %s", (int) cssme.error, errStr);
deleteSSGroup(newSSGroup, nullCred);
throw;
}
catch (MacOSError mose) {
secnotice("integrity", "caught MacOSError during add: %d", (int) mose.osStatus());
deleteSSGroup(newSSGroup, nullCred);
throw;
}
catch (...)
{
secnotice("integrity", "caught unknown exception during add");
deleteSSGroup(newSSGroup, nullCred);
throw;
}
} else {
secinfo("integrity", "modifying the existing SSGroup");
try {
doChange(keychain, recordType, ^{
assert(!newdata);
const AccessCredentials *autoPrompt = globals().itemCredentials();
ssUniqueId->modify(recordType,
mDbAttributes.get(),
newdata,
CSSM_DB_MODIFY_ATTRIBUTE_REPLACE,
autoPrompt);
});
setIntegrity(*ssGroup);
mDbAttributes.reset(NULL);
transaction.commit();
}
catch (CssmError cssme) {
const char* errStr = cssmErrorString(cssme.error);
secnotice("integrity", "caught CssmError during modify: %d %s", (int) cssme.error, errStr);
throw;
}
catch (MacOSError mose) {
secnotice("integrity", "caught MacOSError during modify: %d", (int) mose.osStatus());
throw;
}
catch (...)
{
secnotice("integrity", "caught unknown exception during modify");
throw;
}
}
}
void ItemImpl::deleteSSGroup(SSGroup & ssgroup, const AccessCredentials* nullCred) {
try{
ssgroup->deleteKey(nullCred);
} catch(CssmError error) {
secnotice("integrity", "caught cssm error during deletion of group: %d %s", (int) error.osStatus(), error.what());
} catch(MacOSError error) {
secnotice("integrity", "caught macos error during deletion of group: %d %s", (int) error.osStatus(), error.what());
} catch(UnixError error) {
secnotice("integrity", "caught unix error during deletion of group: %d %s", (int) error.osStatus(), error.what());
}
}
void
ItemImpl::doChange(Keychain keychain, CSSM_DB_RECORDTYPE recordType, void (^tryChange) ())
{
try {
tryChange();
} catch (CssmError cssme) {
unique_ptr<CssmClient::DbAttributes> primaryKeyAttrs;
if(cssme.error == CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA) {
secnotice("integrity", "possible duplicate, trying to delete invalid items");
Keychain kc = (keychain ? keychain : mKeychain);
if(!kc) {
secnotice("integrity", "no valid keychain");
}
if((!kc) || !kc->hasIntegrityProtection()) {
secinfo("integrity", "skipping integrity check for corrupt items due to keychain support");
throw;
} else {
primaryKeyAttrs.reset(getCurrentAttributes());
PrimaryKey pk = kc->makePrimaryKey(recordType, primaryKeyAttrs.get());
bool tryAgain = false;
ItemImpl* maybeItem = kc->_lookupItem(pk);
if(maybeItem) {
if(!maybeItem->checkIntegrity()) {
Item item(maybeItem);
kc->deleteItem(item);
tryAgain = true;
}
} else {
secinfo("integrity", "making a cursor from primary key");
CssmClient::DbCursor cursor = pk->createCursor(kc);
DbUniqueRecord uniqueId;
StLock<Mutex> _mutexLocker(*kc->getKeychainMutex());
unique_ptr<DbAttributes>dbDupAttributes (new DbAttributes(kc->database(), 1));
fillDbAttributesFromSchema(*dbDupAttributes, recordType, kc);
while (cursor->next(dbDupAttributes.get(), NULL, uniqueId)) {
secinfo("integrity", "got an item...");
SSGroup group = safer_cast<SSDbUniqueRecordImpl &>(*uniqueId).group();
if(!ItemImpl::checkIntegrityFromDictionary(*group, dbDupAttributes.get())) {
secnotice("integrity", "item is invalid! deleting...");
uniqueId->deleteRecord();
tryAgain = true;
}
}
}
if(tryAgain) {
secnotice("integrity", "trying again...");
tryChange();
} else {
secnotice("integrity", "duplicate item exception is real; throwing it on");
throw;
}
}
} else {
throw;
}
}
}
void
ItemImpl::getClass(SecKeychainAttribute &attr, UInt32 *actualLength)
{
StLock<Mutex>_(mMutex);
if (actualLength)
*actualLength = sizeof(SecItemClass);
if (attr.length < sizeof(SecItemClass))
MacOSError::throwMe(errSecBufferTooSmall);
SecItemClass aClass = Schema::itemClassFor(recordType());
memcpy(attr.data, &aClass, sizeof(SecItemClass));
}
void
ItemImpl::setAttribute(SecKeychainAttribute& attr)
{
StLock<Mutex>_(mMutex);
setAttribute(Schema::attributeInfo(attr.tag), CssmData(attr.data, attr.length));
}
CSSM_DB_RECORDTYPE
ItemImpl::recordType()
{
StLock<Mutex>_(mMutex);
if (mDbAttributes.get())
return mDbAttributes->recordType();
return mPrimaryKey->recordType();
}
const DbAttributes *
ItemImpl::modifiedAttributes()
{
StLock<Mutex>_(mMutex);
return mDbAttributes.get();
}
const CssmData *
ItemImpl::modifiedData()
{
StLock<Mutex>_(mMutex);
return mData.get();
}
void
ItemImpl::setData(UInt32 length,const void *data)
{
StLock<Mutex>_(mMutex);
mData = new CssmDataContainer(data, length);
}
void
ItemImpl::setAccess(Access *newAccess)
{
StLock<Mutex>_(mMutex);
mAccess = newAccess;
}
CssmClient::DbUniqueRecord
ItemImpl::dbUniqueRecord()
{
StLock<Mutex>_(mMutex);
if (!isPersistent()) {
MacOSError::throwMe(errSecNotAvailable);
}
if (!mUniqueId)
{
DbCursor cursor(mPrimaryKey->createCursor(mKeychain));
if (!cursor->next(NULL, NULL, mUniqueId))
MacOSError::throwMe(errSecInvalidItemRef);
}
if(!(mUniqueId->database() == keychain()->database())) {
secinfo("integrity", "updating db of mUniqueRecord");
DbCursor cursor(mPrimaryKey->createCursor(mKeychain));
if (!cursor->next(NULL, NULL, mUniqueId))
MacOSError::throwMe(errSecInvalidItemRef);
}
return mUniqueId;
}
PrimaryKey
ItemImpl::primaryKey()
{
return mPrimaryKey;
}
bool
ItemImpl::isPersistent()
{
return mKeychain;
}
bool
ItemImpl::isModified()
{
StLock<Mutex>_(mMutex);
return mData.get() || mDbAttributes.get();
}
Keychain
ItemImpl::keychain()
{
return mKeychain;
}
bool
ItemImpl::operator < (const ItemImpl &other)
{
if (mData && *mData)
{
return this < &other;
}
return mPrimaryKey < other.mPrimaryKey;
}
void
ItemImpl::setAttribute(const CssmDbAttributeInfo &info, const CssmPolyData &data)
{
StLock<Mutex>_(mMutex);
if (!mDbAttributes.get())
{
mDbAttributes.reset(new DbAttributes());
mDbAttributes->recordType(mPrimaryKey->recordType());
}
size_t length = data.Length;
const void *buf = reinterpret_cast<const void *>(data.Data);
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;
}
}
mDbAttributes->add(info, CssmData(const_cast<void*>(buf), length));
}
void
ItemImpl::modifyContent(const SecKeychainAttributeList *attrList, UInt32 dataLength, const void *inData)
{
StLock<Mutex>_(mMutex);
StMaybeLock<Mutex>__ (mKeychain == NULL ? NULL : mKeychain->getKeychainMutex());
if (!mDbAttributes.get())
{
mDbAttributes.reset(new DbAttributes());
mDbAttributes->recordType(mPrimaryKey->recordType());
}
if(attrList) {
for(UInt32 ix=0; ix < attrList->count; ix++)
{
SecKeychainAttrType attrTag = attrList->attr[ix].tag;
if (attrTag == APPLEDB_CSSM_PRINTNAME_ATTRIBUTE)
{
attrTag = kSecLabelItemAttr;
}
mDbAttributes->add(Schema::attributeInfo(attrTag), CssmData(attrList->attr[ix].data, attrList->attr[ix].length));
}
}
if(inData)
{
mData = new CssmDataContainer(inData, dataLength);
}
update();
}
void
ItemImpl::getContent(SecItemClass *itemClass, SecKeychainAttributeList *attrList, UInt32 *length, void **outData)
{
StLock<Mutex>_(mMutex);
if (!mKeychain && outData)
{
CssmData *data = mData.get();
if (!data)
MacOSError::throwMe(errSecDataNotAvailable);
}
if (itemClass)
*itemClass = Schema::itemClassFor(recordType());
bool getDataFromDatabase = mKeychain && mPrimaryKey;
if (getDataFromDatabase) {
dbUniqueRecord();
UInt32 attrCount = attrList ? attrList->count : 0;
DbAttributes dbAttributes(dbUniqueRecord()->database(), attrCount);
for (UInt32 ix = 0; ix < attrCount; ++ix)
{
dbAttributes.add(Schema::attributeInfo(attrList->attr[ix].tag));
}
CssmDataContainer itemData;
getContent(&dbAttributes, outData ? &itemData : NULL);
for (UInt32 ix = 0; ix < attrCount; ++ix)
{
if (dbAttributes.at(ix).NumberOfValues > 0)
{
attrList->attr[ix].data = dbAttributes.at(ix).Value[0].Data;
attrList->attr[ix].length = (UInt32)dbAttributes.at(ix).Value[0].Length;
dbAttributes.at(ix).Value[0].Data = NULL;
dbAttributes.at(ix).Value[0].Length = 0;
}
else
{
attrList->attr[ix].data = NULL;
attrList->attr[ix].length = 0;
}
}
if (outData)
{
*outData=itemData.data();
itemData.Data = NULL;
if (length)
*length=(UInt32)itemData.length();
itemData.Length = 0;
}
}
else
{
getLocalContent(attrList, length, outData);
}
}
void
ItemImpl::freeContent(SecKeychainAttributeList *attrList, void *data)
{
Allocator &allocator = Allocator::standard(Allocator::sensitive); if (data)
allocator.free(data);
UInt32 attrCount = attrList ? attrList->count : 0;
for (UInt32 ix = 0; ix < attrCount; ++ix)
{
allocator.free(attrList->attr[ix].data);
attrList->attr[ix].data = NULL;
}
}
void
ItemImpl::modifyAttributesAndData(const SecKeychainAttributeList *attrList, UInt32 dataLength, const void *inData)
{
StLock<Mutex>_(mMutex);
if (!mKeychain)
MacOSError::throwMe(errSecNoSuchKeychain);
if (!mDoNotEncrypt)
{
if (!mDbAttributes.get())
{
mDbAttributes.reset(new DbAttributes());
mDbAttributes->recordType(mPrimaryKey->recordType());
}
CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType();
UInt32 attrCount = attrList ? attrList->count : 0;
for (UInt32 ix = 0; ix < attrCount; ix++)
{
SecKeychainAttrType attrTag = attrList->attr[ix].tag;
if (attrTag == kSecLabelItemAttr)
{
if (IS_PASSWORD_ITEM_CLASS( Schema::itemClassFor(recordType) ))
attrTag = APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE;
}
CssmDbAttributeInfo info=mKeychain->attributeInfoFor(recordType, attrTag);
if (attrList->attr[ix].length || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_STRING || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_BLOB
|| info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_STRING || info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_BIG_NUM
|| info.AttributeFormat==CSSM_DB_ATTRIBUTE_FORMAT_MULTI_UINT32)
mDbAttributes->add(info, CssmData(attrList->attr[ix].data, attrList->attr[ix].length));
else
mDbAttributes->add(info);
}
}
if(inData)
{
mData = new CssmDataContainer(inData, dataLength);
}
update();
}
void
ItemImpl::getAttributesAndData(SecKeychainAttributeInfo *info, SecItemClass *itemClass,
SecKeychainAttributeList **attrList, UInt32 *length, void **outData)
{
StLock<Mutex>_(mMutex);
if (!mKeychain && outData)
{
CssmData *data = mData.get();
if (!data)
MacOSError::throwMe(errSecDataNotAvailable);
}
SecItemClass myItemClass = Schema::itemClassFor(recordType());
if (itemClass)
*itemClass = myItemClass;
dbUniqueRecord();
UInt32 attrCount = info ? info->count : 0;
DbAttributes dbAttributes(dbUniqueRecord()->database(), attrCount);
for (UInt32 ix = 0; ix < attrCount; ix++)
{
CssmDbAttributeData &record = dbAttributes.add();
record.Info.AttributeNameFormat=CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
record.Info.Label.AttributeID=info->tag[ix];
if (record.Info.Label.AttributeID == kSecLabelItemAttr)
{
if (IS_PASSWORD_ITEM_CLASS( myItemClass ))
record.Info.Label.AttributeID = APPLEDB_GENERIC_PRINTNAME_ATTRIBUTE;
}
}
CssmDataContainer itemData;
getContent(&dbAttributes, outData ? &itemData : NULL);
if (info && attrList)
{
SecKeychainAttributeList *theList=reinterpret_cast<SecKeychainAttributeList *>(malloc(sizeof(SecKeychainAttributeList)));
if(attrCount == 0) {
theList->count = 0;
theList->attr = NULL;
} else {
SecKeychainAttribute *attr=reinterpret_cast<SecKeychainAttribute *>(calloc(attrCount, sizeof(SecKeychainAttribute)));
theList->count=attrCount;
theList->attr=attr;
for (UInt32 ix = 0; ix < attrCount; ++ix)
{
attr[ix].tag=info->tag[ix];
if (dbAttributes.at(ix).NumberOfValues > 0)
{
attr[ix].data = dbAttributes.at(ix).Value[0].Data;
attr[ix].length = (UInt32)dbAttributes.at(ix).Value[0].Length;
dbAttributes.at(ix).Value[0].Data = NULL;
dbAttributes.at(ix).Value[0].Length = 0;
}
else
{
attr[ix].data = NULL;
attr[ix].length = 0;
}
}
}
*attrList=theList;
}
if (outData)
{
*outData=itemData.data();
itemData.Data=NULL;
if (length) *length=(UInt32)itemData.length();
itemData.Length=0;
}
}
void
ItemImpl::freeAttributesAndData(SecKeychainAttributeList *attrList, void *data)
{
Allocator &allocator = Allocator::standard(Allocator::sensitive);
if (data)
allocator.free(data);
if (attrList)
{
for (UInt32 ix = 0; ix < attrList->count; ++ix)
{
allocator.free(attrList->attr[ix].data);
}
free(attrList->attr);
free(attrList);
}
}
void
ItemImpl::getAttribute(SecKeychainAttribute& attr, UInt32 *actualLength)
{
StLock<Mutex>_(mMutex);
if (attr.tag == kSecClassItemAttr)
return getClass(attr, actualLength);
if (mDbAttributes.get())
{
CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(attr.tag));
if (data)
{
getAttributeFrom(data, attr, actualLength);
return;
}
}
if (!mKeychain)
MacOSError::throwMe(errSecNoSuchAttr);
DbAttributes dbAttributes(dbUniqueRecord()->database(), 1);
dbAttributes.add(Schema::attributeInfo(attr.tag));
dbUniqueRecord()->get(&dbAttributes, NULL);
getAttributeFrom(&dbAttributes.at(0), attr, actualLength);
}
void
ItemImpl::getAttributeFrom(CssmDbAttributeData *data, SecKeychainAttribute &attr, UInt32 *actualLength)
{
StLock<Mutex>_(mMutex);
static const uint32 zero = 0;
UInt32 length;
const void *buf = NULL;
sint64 macLDT;
uint32 macSeconds;
sint16 svalue16;
uint16 uvalue16;
sint8 svalue8;
uint8 uvalue8;
if (!data)
length = 0;
else if (data->size() < 1) {
if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32
|| data->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
{
length = sizeof(zero);
buf = &zero;
}
else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
length = 0; else length = 0;
}
else {
length = (UInt32)data->Value[0].Length;
buf = data->Value[0].Data;
if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_SINT32)
{
if (attr.length == sizeof(sint8))
{
length = attr.length;
svalue8 = sint8(*reinterpret_cast<const sint32 *>(buf));
buf = &svalue8;
}
else if (attr.length == sizeof(sint16))
{
length = attr.length;
svalue16 = sint16(*reinterpret_cast<const sint32 *>(buf));
buf = &svalue16;
}
}
else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_UINT32)
{
if (attr.length == sizeof(uint8))
{
length = attr.length;
uvalue8 = uint8(*reinterpret_cast<const uint32 *>(buf));
buf = &uvalue8;
}
else if (attr.length == sizeof(uint16))
{
length = attr.length;
uvalue16 = uint16(*reinterpret_cast<const uint32 *>(buf));
buf = &uvalue16;
}
}
else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
{
if (attr.length == sizeof(uint32))
{
TimeStringToMacSeconds(data->Value[0], macSeconds);
buf = &macSeconds;
length = attr.length;
}
else if (attr.length == sizeof(sint64))
{
TimeStringToMacLongDateTime(data->Value[0], macLDT);
buf = &macLDT;
length = attr.length;
}
}
}
if (actualLength)
*actualLength = length;
if (length)
{
if (attr.length < length)
MacOSError::throwMe(errSecBufferTooSmall);
memcpy(attr.data, buf, length);
}
}
void
ItemImpl::getData(CssmDataContainer& outData)
{
StLock<Mutex>_(mMutex);
if (!mKeychain)
{
CssmData *data = mData.get();
if (!data)
MacOSError::throwMe(errSecDataNotAvailable);
outData = *data;
return;
}
getContent(NULL, &outData);
}
SSGroup
ItemImpl::group()
{
StLock<Mutex>_(mMutex);
SSGroup group;
if (!!mUniqueId)
{
Db db(mKeychain->database());
if (useSecureStorage(db))
{
group = safer_cast<SSDbUniqueRecordImpl &>(*dbUniqueRecord()).group();
}
}
return group;
}
void ItemImpl::getLocalContent(SecKeychainAttributeList *attributeList, UInt32 *outLength, void **outData)
{
StLock<Mutex>_(mMutex);
willRead();
Allocator &allocator = Allocator::standard(); if (outData)
{
CssmData *data = mData.get();
if (!data)
MacOSError::throwMe(errSecDataNotAvailable);
UInt32 length = (UInt32)data->Length;
*outData = allocator.malloc(length);
memcpy(*outData, data->Data, length);
if (outLength)
*outLength = length;
}
if (attributeList)
{
if (!mDbAttributes.get())
MacOSError::throwMe(errSecDataNotAvailable);
for (UInt32 ix = 0; ix < attributeList->count; ++ix)
{
SecKeychainAttribute &attribute = attributeList->attr[ix];
CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(attribute.tag));
if (data && data->NumberOfValues > 0)
{
UInt32 length = (UInt32)data->Value[0].Length;
attribute.data = allocator.malloc(length);
memcpy(attribute.data, data->Value[0].Data, length);
attribute.length = length;
}
else
{
attribute.length = 0;
attribute.data = NULL;
}
}
}
}
void
ItemImpl::getContent(DbAttributes *dbAttributes, CssmDataContainer *itemData)
{
StLock<Mutex>_(mMutex);
if (itemData)
{
Db db(dbUniqueRecord()->database());
if (mDoNotEncrypt)
{
dbUniqueRecord()->getWithoutEncryption (dbAttributes, itemData);
return;
}
if (useSecureStorage(db))
{
try {
if(!checkIntegrity()) {
secnotice("integrity", "item has no integrity, denying access");
CssmError::throwMe(errSecInvalidItemRef);
}
} catch(CssmError cssme) {
secnotice("integrity", "error while checking integrity, denying access: %s", cssme.what());
throw;
}
SSDbUniqueRecordImpl* impl = dynamic_cast<SSDbUniqueRecordImpl *>(&(*dbUniqueRecord()));
if (impl == NULL)
{
CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
}
SSDbUniqueRecord ssUniqueId(impl);
const AccessCredentials *autoPrompt = globals().itemCredentials();
ssUniqueId->get(dbAttributes, itemData, autoPrompt);
return;
}
}
dbUniqueRecord()->get(dbAttributes, itemData);
}
bool
ItemImpl::useSecureStorage(const Db &db)
{
StLock<Mutex>_(mMutex);
switch (recordType())
{
case CSSM_DL_DB_RECORD_GENERIC_PASSWORD:
case CSSM_DL_DB_RECORD_INTERNET_PASSWORD:
case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD:
if (db->dl()->subserviceMask() & CSSM_SERVICE_CSP)
return true;
break;
default:
break;
}
return false;
}
void ItemImpl::willRead()
{
}
Item ItemImpl::makeFromPersistentReference(const CFDataRef persistentRef, bool *isIdentityRef)
{
CssmData dictData((void*)::CFDataGetBytePtr(persistentRef), ::CFDataGetLength(persistentRef));
NameValueDictionary dict(dictData);
Keychain keychain;
Item item = (ItemImpl *) NULL;
if (isIdentityRef) {
*isIdentityRef = (dict.FindByName(IDENTITY_KEY) != 0) ? true : false;
}
if (dict.FindByName(SSUID_KEY) != 0)
{
DLDbIdentifier dlDbIdentifier = NameValueDictionary::MakeDLDbIdentifierFromNameValueDictionary(dict);
DLDbIdentifier newDlDbIdentifier(dlDbIdentifier.ssuid(),
DLDbListCFPref::ExpandTildesInPath(dlDbIdentifier.dbName()).c_str(),
dlDbIdentifier.dbLocation());
keychain = globals().storageManager.keychain(newDlDbIdentifier);
const NameValuePair* aDictItem = dict.FindByName(ITEM_KEY);
if (aDictItem && keychain)
{
PrimaryKey primaryKey(aDictItem->Value());
item = keychain->item(primaryKey);
}
}
KCThrowIf_( !item, errSecItemNotFound );
return item;
}
void ItemImpl::copyPersistentReference(CFDataRef &outDataRef, bool isSecIdentityRef)
{
if (secd_PersistentRef) {
outDataRef = secd_PersistentRef;
return;
}
StLock<Mutex>_(mMutex);
if (!mKeychain || !mPrimaryKey) {
MacOSError::throwMe(errSecItemNotFound);
}
DLDbIdentifier dlDbIdentifier = mKeychain->dlDbIdentifier();
DLDbIdentifier newDlDbIdentifier(dlDbIdentifier.ssuid(),
DLDbListCFPref::AbbreviatedPath(mKeychain->name()).c_str(),
dlDbIdentifier.dbLocation());
NameValueDictionary dict;
NameValueDictionary::MakeNameValueDictionaryFromDLDbIdentifier(newDlDbIdentifier, dict);
CssmData* pKey = mPrimaryKey;
dict.Insert (new NameValuePair(ITEM_KEY, *pKey));
if (isSecIdentityRef) {
uint32_t value = -1;
CssmData valueData((void*)&value, sizeof(value));
dict.Insert (new NameValuePair(IDENTITY_KEY, valueData));
}
CssmData dictData;
dict.Export(dictData);
outDataRef = ::CFDataCreate(kCFAllocatorDefault, dictData.Data, dictData.Length);
free (dictData.Data);
}
void ItemImpl::copyRecordIdentifier(CSSM_DATA &data)
{
StLock<Mutex>_(mMutex);
CssmClient::DbUniqueRecord uniqueRecord = dbUniqueRecord ();
uniqueRecord->getRecordIdentifier(data);
}
const CssmData &ItemImpl::itemID()
{
StLock<Mutex>_(mMutex);
if(mPrimaryKey->length() == 0) {
MacOSError::throwMe(errSecNoSuchAttr);
}
return *mPrimaryKey;
}
bool ItemImpl::equal(SecCFObject &other)
{
ItemImpl& other_item = (ItemImpl&)other;
if (mPrimaryKey != NULL && mPrimaryKey == other_item.mPrimaryKey)
{
return true;
}
CFHashCode this_hash = hash();
CFHashCode other_hash = other.hash();
return (this_hash == other_hash);
}
CFHashCode ItemImpl::hash()
{
CFHashCode result = SecCFObject::hash();
StLock<Mutex>_(mMutex);
RefPointer<CssmDataContainer> data_to_hash;
if (mData && *mData)
{
data_to_hash = mData;
}
if (NULL != data_to_hash.get())
{
CFDataRef temp_data = NULL;
unsigned char digest[CC_SHA256_DIGEST_LENGTH];
if (data_to_hash->length() < 80)
{
temp_data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
(const UInt8 *)data_to_hash->data(), data_to_hash->length(), kCFAllocatorNull);
}
else
{
memset(digest, 0, CC_SHA256_DIGEST_LENGTH);
CC_SHA256((const void *)data_to_hash->data(), (CC_LONG)data_to_hash->length(), digest);
temp_data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
(const UInt8 *)digest, CC_SHA256_DIGEST_LENGTH, kCFAllocatorNull);
}
if (NULL != temp_data)
{
result = CFHash(temp_data);
CFRelease(temp_data);
}
}
return result;
}
void ItemImpl::postItemEvent(SecKeychainEvent theEvent)
{
mKeychain->postEvent(theEvent, this);
}
Item::Item()
{
}
Item::Item(ItemImpl *impl) : SecPointer<ItemImpl>(impl)
{
}
Item::Item(SecItemClass itemClass, OSType itemCreator, UInt32 length, const void* data, bool inhibitCheck)
{
if (!inhibitCheck)
{
if (itemClass == CSSM_DL_DB_RECORD_X509_CERTIFICATE
|| itemClass == CSSM_DL_DB_RECORD_PUBLIC_KEY
|| itemClass == CSSM_DL_DB_RECORD_PRIVATE_KEY
|| itemClass == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
MacOSError::throwMe(errSecNoSuchClass);
}
*this = new ItemImpl(itemClass, itemCreator, length, data, inhibitCheck);
}
Item::Item(SecItemClass itemClass, SecKeychainAttributeList *attrList, UInt32 length, const void* data)
{
*this = new ItemImpl(itemClass, attrList, length, data);
}
Item::Item(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId)
: SecPointer<ItemImpl>(
primaryKey->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
? Certificate::make(keychain, primaryKey, uniqueId)
: (primaryKey->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
|| primaryKey->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
|| primaryKey->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
? KeyItem::make(keychain, primaryKey, uniqueId)
: primaryKey->recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
? ExtendedAttribute::make(keychain, primaryKey, uniqueId)
: ItemImpl::make(keychain, primaryKey, uniqueId))
{
}
Item::Item(const Keychain &keychain, const PrimaryKey &primaryKey)
: SecPointer<ItemImpl>(
primaryKey->recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
? Certificate::make(keychain, primaryKey)
: (primaryKey->recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
|| primaryKey->recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
|| primaryKey->recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
? KeyItem::make(keychain, primaryKey)
: primaryKey->recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
? ExtendedAttribute::make(keychain, primaryKey)
: ItemImpl::make(keychain, primaryKey))
{
}
Item::Item(ItemImpl &item)
: SecPointer<ItemImpl>(
item.recordType() == CSSM_DL_DB_RECORD_X509_CERTIFICATE
? new Certificate(safer_cast<Certificate &>(item))
: (item.recordType() == CSSM_DL_DB_RECORD_PUBLIC_KEY
|| item.recordType() == CSSM_DL_DB_RECORD_PRIVATE_KEY
|| item.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
? new KeyItem(safer_cast<KeyItem &>(item))
: item.recordType() == CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE
? new ExtendedAttribute(safer_cast<ExtendedAttribute &>(item))
: new ItemImpl(item))
{
}
CFIndex KeychainCore::GetItemRetainCount(Item& item)
{
return CFGetRetainCount(item->handle(false));
}
void ItemImpl::setPersistentRef(CFDataRef ref)
{
if (secd_PersistentRef) {
CFRelease(secd_PersistentRef);
}
secd_PersistentRef = ref;
CFRetain(ref);
}
CFDataRef ItemImpl::getPersistentRef()
{
return secd_PersistentRef;
}
bool ItemImpl::mayDelete()
{
ObjectImpl* uniqueIDImpl = mUniqueId.get();
if (uniqueIDImpl != NULL)
{
bool result = mUniqueId->isIdle();
return result;
}
else
{
return true;
}
}