X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/e3d460c9de4426da6c630c3ae3f46173a99f82d8..07691282a056c4efea71e1e505527601e8cc166b:/OSX/libsecurity_keychain/lib/Item.cpp diff --git a/OSX/libsecurity_keychain/lib/Item.cpp b/OSX/libsecurity_keychain/lib/Item.cpp index 025c8b3a..bea27fec 100644 --- a/OSX/libsecurity_keychain/lib/Item.cpp +++ b/OSX/libsecurity_keychain/lib/Item.cpp @@ -65,6 +65,31 @@ using namespace CSSMDateTimeUtils; // ItemImpl // +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(KeyItem::fromSecKeyRef(ptr)); + } else if (SecCFObject *p = SecCFObject::optional(ptr)) { + if (ItemImpl *pp = dynamic_cast(p)) { + return pp; + } else { + MacOSError::throwMe(errSecInvalidItemRef); + } + } else { + return NULL; + } +} + // NewItemImpl constructor ItemImpl::ItemImpl(SecItemClass itemClass, OSType itemCreator, UInt32 length, const void* data, bool dontDoAttributes) : mDbAttributes(new DbAttributes()), @@ -156,7 +181,8 @@ ItemImpl::ItemImpl(ItemImpl &item) : if (item.mKeychain) { // get the entire source item from its keychain. This requires figuring // out the schema for the item based on its record type. - fillDbAttributesFromSchema(*mDbAttributes, item.recordType()); + // Ask the remote item to fill our attributes dictionary, because it probably has an attached keychain to ask + item.fillDbAttributesFromSchema(*mDbAttributes, item.recordType()); item.getContent(mDbAttributes.get(), mData.get()); } @@ -170,16 +196,24 @@ ItemImpl::ItemImpl(ItemImpl &item) : } ItemImpl::~ItemImpl() -{ +try { if (secd_PersistentRef) { CFRelease(secd_PersistentRef); } +} catch (...) { +#ifndef NDEBUG + /* if we get an exception in destructor, presumably the mutex, lets throw if we + * are in a debug build (ie reach end of block) */ +#else + return; +#endif } + Mutex* -ItemImpl::getMutexForObject() +ItemImpl::getMutexForObject() const { if (mKeychain.get()) { @@ -249,7 +283,7 @@ void ItemImpl::fillDbAttributesFromSchema(DbAttributes& dbAttributes, CSSM_DB_RE SecKeychainAttributeInfo* infos; keychain->getAttributeInfoForItemID(recordType, &infos); - secdebugfunc("integrity", "filling %u attributes for type %u", (unsigned int)infos->count, recordType); + 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; @@ -267,27 +301,27 @@ void ItemImpl::fillDbAttributesFromSchema(DbAttributes& dbAttributes, CSSM_DB_RE DbAttributes* ItemImpl::getCurrentAttributes() { DbAttributes* dbAttributes; - secdebugfunc("integrity", "getting current attributes..."); + secinfo("integrity", "getting current attributes..."); if(mUniqueId.get()) { // If we have a unique id, there's an item in the database backing us. Ask for its attributes. - dbAttributes = new DbAttributes(mUniqueId->database(), 1); + dbAttributes = new DbAttributes(dbUniqueRecord()->database(), 1); fillDbAttributesFromSchema(*dbAttributes, recordType()); mUniqueId->get(dbAttributes, NULL); // and fold in any updates. if(mDbAttributes.get()) { - secdebugfunc("integrity", "adding %d attributes from mDbAttributes", mDbAttributes->size()); + secinfo("integrity", "adding %d attributes from mDbAttributes", mDbAttributes->size()); dbAttributes->updateWithDbAttributes(&(*mDbAttributes.get())); } } else if (mDbAttributes.get()) { // We don't have a backing item, so all our attributes are in mDbAttributes. Copy them. - secdebugfunc("integrity", "no unique id, using %d attributes from mDbAttributes", mDbAttributes->size()); + secnotice("integrity", "no unique id, using %d attributes from mDbAttributes", mDbAttributes->size()); dbAttributes = new DbAttributes(); dbAttributes->updateWithDbAttributes(&(*mDbAttributes.get())); } else { // No attributes at all. We should maybe throw here, but let's not. - secdebugfunc("integrity", "no attributes at all"); + secnotice("integrity", "no attributes at all"); dbAttributes = new DbAttributes(); } dbAttributes->recordType(recordType()); @@ -309,7 +343,7 @@ void ItemImpl::encodeAttributesFromDictionary(CssmOwnedData &attributeBlob, DbAt CFRef attributes; attributes.take(CFDictionaryCreateMutable(NULL, dbAttributes->size(), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); - secdebugfunc("integrity", "looking at %d attributes", dbAttributes->size()); + secinfo("integrity", "looking at %d attributes", dbAttributes->size()); // TODO: include record type and semantic information? for(int i = 0; i < dbAttributes->size(); i++) { @@ -435,19 +469,17 @@ void ItemImpl::computeDigestFromDictionary(CssmOwnedData &sha2, DbAttributes* db sha2.length(CC_SHA256_DIGEST_LENGTH); CC_SHA256(attributeBlob.get().data(), static_cast(attributeBlob.get().length()), sha2); - secdebugfunc("integrity", "finished: %s", sha2.get().toHex().c_str()); + secinfo("integrity", "finished: %s", sha2.get().toHex().c_str()); } catch (MacOSError mose) { - secdebugfunc("integrity", "MacOSError: %d", (int)mose.osStatus()); + secnotice("integrity", "MacOSError: %d", (int)mose.osStatus()); } catch (...) { - secdebugfunc("integrity", "unknown exception"); + secnotice("integrity", "unknown exception"); } } void ItemImpl::addIntegrity(Access &access, bool force) { - secdebugfunc("integrity", "called"); - if(!force && (!mKeychain || !mKeychain->hasIntegrityProtection())) { - secdebugfunc("integrity", "skipping integrity add due to keychain version\n"); + secinfo("integrity", "skipping integrity add due to keychain version\n"); return; } @@ -462,17 +494,17 @@ void ItemImpl::addIntegrity(Access &access, bool force) { if(acls.size() >= 1) { // Use the existing ACL acl = acls[0]; - secdebugfunc("integrity", "previous integrity acl exists; setting integrity"); + secinfo("integrity", "previous integrity acl exists; setting integrity"); acl->setIntegrity(digest.get()); // Delete all extra ACLs for(int i = 1; i < acls.size(); i++) { - secdebugfunc("integrity", "extra integrity acls exist; removing %d",i); + secnotice("integrity", "extra integrity acls exist; removing %d",i); acls[i]->remove(); } } else if(acls.size() == 0) { // Make a new ACL - secdebugfunc("integrity", "no previous integrity acl exists; making a new one"); + secnotice("integrity", "no previous integrity acl exists; making a new one"); acl = new ACL(digest.get()); access.add(acl); } @@ -480,7 +512,7 @@ void ItemImpl::addIntegrity(Access &access, bool force) { void ItemImpl::setIntegrity(bool force) { if(!force && (!mKeychain || !mKeychain->hasIntegrityProtection())) { - secdebugfunc("integrity", "skipping integrity set due to keychain version"); + secnotice("integrity", "skipping integrity set due to keychain version"); return; } @@ -500,7 +532,7 @@ void ItemImpl::addIntegrity(Access &access, bool force) { void ItemImpl::setIntegrity(AclBearer &bearer, bool force) { if(!force && (!mKeychain || !mKeychain->hasIntegrityProtection())) { - secdebugfunc("integrity", "skipping integrity acl set due to keychain version"); + secnotice("integrity", "skipping integrity acl set due to keychain version"); return; } @@ -511,17 +543,38 @@ void ItemImpl::setIntegrity(AclBearer &bearer, bool force) { access->setAccess(bearer, true); } +void ItemImpl::removeIntegrity(const AccessCredentials *cred) { + removeIntegrity(*group(), cred); +} + +void ItemImpl::removeIntegrity(AclBearer &bearer, const AccessCredentials *cred) { + SecPointer access = new Access(bearer); + vector 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() { // Note: subclasses are responsible for checking themselves. // If we don't have a keychain yet, we don't have any group. Return true? if(!isPersistent()) { - secdebugfunc("integrity", "no keychain, integrity is valid?"); + secnotice("integrity", "no keychain, integrity is valid?"); return true; } if(!mKeychain || !mKeychain->hasIntegrityProtection()) { - secdebugfunc("integrity", "skipping integrity check due to keychain version"); + secinfo("integrity", "skipping integrity check due to keychain version"); return true; } @@ -538,7 +591,7 @@ bool ItemImpl::checkIntegrity() { bool ItemImpl::checkIntegrity(AclBearer& aclBearer) { if(!mKeychain || !mKeychain->hasIntegrityProtection()) { - secdebugfunc("integrity", "skipping integrity check due to keychain version"); + secinfo("integrity", "skipping integrity check due to keychain version"); return true; } @@ -559,7 +612,7 @@ bool ItemImpl::checkIntegrityFromDictionary(AclBearer& aclBearer, DbAttributes* auto_ptr acl(new ACL(info, Allocator::standard())); for(int i = 1; i < aclInfos.count(); i++) { - secdebugfunc("integrity", "*** DUPLICATE INTEGRITY ACL, something has gone wrong"); + secnotice("integrity", "*** DUPLICATE INTEGRITY ACL, something has gone wrong"); } CssmAutoData digest(Allocator::standard()); @@ -570,7 +623,7 @@ bool ItemImpl::checkIntegrityFromDictionary(AclBearer& aclBearer, DbAttributes* } catch (CssmError cssme) { const char* errStr = cssmErrorString(cssme.error); - secdebugfunc("integrity", "caught CssmError: %d %s", (int) cssme.error, errStr); + secnotice("integrity", "caught CssmError: %d %s", (int) cssme.error, errStr); if(cssme.error == CSSMERR_CSP_ACL_ENTRY_TAG_NOT_FOUND) { // TODO: No entry, run migrator? @@ -579,17 +632,18 @@ bool ItemImpl::checkIntegrityFromDictionary(AclBearer& aclBearer, DbAttributes* if(cssme.error == CSSMERR_CSP_INVALID_ACL_SUBJECT_VALUE) { // something went horribly wrong with fetching acl. - secdebugfunc("integrity", "INVALID ITEM (too many integrity acls)"); + secnotice("integrity", "INVALID ITEM (too many integrity acls)"); return false; } if(cssme.error == CSSMERR_CSP_VERIFY_FAILED) { - secdebugfunc("integrity", "MAC verification failed; something has gone very wrong"); + secnotice("integrity", "MAC verification failed; something has gone very wrong"); + return false; // No MAC, no integrity. } - throw cssme; + throw; } - secdebugfunc("integrity", "***** INVALID ITEM"); + secnotice("integrity", "***** INVALID ITEM"); return false; } @@ -626,6 +680,7 @@ PrimaryKey ItemImpl::addWithCopyInfo (Keychain &keychain, bool isCopy) } // If the label (PrintName) attribute isn't specified, set a default label. + mDbAttributes->canonicalize(); // make sure we'll find the label with the thing Schema::attributeInfo returns if (!mDoNotEncrypt && !mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr))) { // if doNotEncrypt was set all of the attributes are wrapped in the data blob. Don't calculate here. @@ -684,6 +739,7 @@ PrimaryKey ItemImpl::addWithCopyInfo (Keychain &keychain, bool isCopy) try { mKeychain = keychain; + StLock_(*(mKeychain->getKeychainMutex())); // must hold this mutex before calling db->insert Db db(keychain->database()); if (mDoNotEncrypt) @@ -727,17 +783,31 @@ ItemImpl::add (Keychain &keychain) Item ItemImpl::copyTo(const Keychain &keychain, Access *newAccess) { + // We'll be removing any Partition or Integrity ACLs from this item during + // the copy. Note that creating a new item from this one fetches the data, + // so this process must now be on the ACL/partition ID list for this item, + // and an attacker without access can't cause this removal. + // + // The integrity and partition ID acls will get re-added once the item lands + // in the new keychain, if it supports them. If it doesn't, removing the + // integrity acl as it leaves will prevent any issues if the item is + // modified in the unsupported keychain and then re-copied back into an + // integrity keychain. + StLock_(mMutex); Item item(*this); - if (newAccess) + if (newAccess) { + newAccess->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID); + newAccess->removeAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY); item->setAccess(newAccess); - else - { + } else { /* Attempt to copy the access from the current item to the newly created one. */ SSGroup myGroup = group(); if (myGroup) { SecPointer access = new Access(*myGroup); + access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID); + access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY); item->setAccess(access); } } @@ -757,6 +827,9 @@ ItemImpl::update() if (!isModified()) return; + // Hold this before modifying the db below + StLock__(*(mKeychain->getKeychainMutex())); + CSSM_DB_RECORDTYPE aRecordType = recordType(); KeychainSchema schema = mKeychain->keychainSchema(); @@ -768,19 +841,17 @@ ItemImpl::update() setAttribute(schema->attributeInfoFor(aRecordType, kSecModDateItemAttr), date); } - // Make sure that we have mUniqueId - dbUniqueRecord(); - Db db(mUniqueId->database()); + Db db(dbUniqueRecord()->database()); if (mDoNotEncrypt) { CSSM_DB_RECORD_ATTRIBUTE_DATA attrData; memset (&attrData, 0, sizeof (attrData)); attrData.DataRecordType = aRecordType; - mUniqueId->modifyWithoutEncryption(aRecordType, - &attrData, - mData.get(), - CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); + dbUniqueRecord()->modifyWithoutEncryption(aRecordType, + &attrData, + mData.get(), + CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); } else if (useSecureStorage(db)) { @@ -793,10 +864,10 @@ ItemImpl::update() } else { - mUniqueId->modify(aRecordType, - mDbAttributes.get(), - mData.get(), - CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); + dbUniqueRecord()->modify(aRecordType, + mDbAttributes.get(), + mData.get(), + CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); } if (!mDoNotEncrypt) @@ -820,8 +891,6 @@ ItemImpl::updateSSGroup(Db& db, CSSM_DB_RECORDTYPE recordType, CssmDataContainer AclFactory aclFactory; const AccessCredentials *nullCred = aclFactory.nullCred(); - secdebugfunc("integrity", "called"); - bool haveOldUniqueId = !!mUniqueId.get(); SSDbUniqueRecord ssUniqueId(NULL); SSGroup ssGroup(NULL); @@ -839,7 +908,7 @@ ItemImpl::updateSSGroup(Db& db, CSSM_DB_RECORDTYPE recordType, CssmDataContainer // If there aren't any attributes, make up some blank ones. if (!mDbAttributes.get()) { - secdebugfunc("integrity", "making new dbattributes"); + secinfo("integrity", "making new dbattributes"); mDbAttributes.reset(new DbAttributes()); mDbAttributes->recordType(mPrimaryKey->recordType()); } @@ -857,7 +926,7 @@ ItemImpl::updateSSGroup(Db& db, CSSM_DB_RECORDTYPE recordType, CssmDataContainer if ((!access) && (haveOldUniqueId)) { // Copy the ACL from the old group. - secdebugfunc("integrity", "copying old ACL"); + secinfo("integrity", "copying old ACL"); access = new Access(*(ssGroup)); // We can't copy these over to the new item; they're going to be reset. @@ -865,27 +934,13 @@ ItemImpl::updateSSGroup(Db& db, CSSM_DB_RECORDTYPE recordType, CssmDataContainer access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID); access->removeAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY); } else if (!access) { - secdebugfunc("integrity", "setting up new ACL"); + secinfo("integrity", "setting up new ACL"); // create default access controls for the new item CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr)); string printName = data ? CssmData::overlay(data->Value[0]).toString() : "keychain item"; access = new Access(printName); - - // special case for "iTools" password - allow anyone to decrypt the item - if (recordType == CSSM_DL_DB_RECORD_GENERIC_PASSWORD) - { - CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(kSecServiceItemAttr)); - if (data && data->Value[0].Length == 6 && !memcmp("iTools", data->Value[0].Data, 6)) - { - typedef vector > AclSet; - AclSet acls; - access->findAclsForRight(CSSM_ACL_AUTHORIZATION_DECRYPT, acls); - for (AclSet::const_iterator it = acls.begin(); it != acls.end(); it++) - (*it)->form(ACL::allowAllForm); - } - } } else { - secdebugfunc("integrity", "passed an Access, use it"); + secinfo("integrity", "passed an Access, use it"); // Access is non-null. Do nothing. } @@ -905,17 +960,17 @@ ItemImpl::updateSSGroup(Db& db, CSSM_DB_RECORDTYPE recordType, CssmDataContainer maker.initialOwner(prototype, nullCred); if(saveToNewSSGroup) { - secdebugfunc("integrity", "saving to a new SSGroup"); + secinfo("integrity", "saving to a new SSGroup"); // If we're updating an item, it has an old group and possibly an // old mUniqueId. Delete these from the database, so we can insert // new ones. if(haveOldUniqueId) { - secdebugfunc("integrity", "deleting old mUniqueId"); + secinfo("integrity", "deleting old mUniqueId"); mUniqueId->deleteRecord(); mUniqueId.release(); } else { - secdebugfunc("integrity", "no old mUniqueId"); + secinfo("integrity", "no old mUniqueId"); } // Create a new SSGroup with temporary access controls @@ -924,8 +979,7 @@ ItemImpl::updateSSGroup(Db& db, CSSM_DB_RECORDTYPE recordType, CssmDataContainer try { doChange(keychain, recordType, ^{ - mUniqueId = ssDb->insert(recordType, mDbAttributes.get(), - newdata, newSSGroup, cred); + mUniqueId = ssDb->ssInsert(recordType, mDbAttributes.get(), newdata, newSSGroup, cred); }); // now finalize the access controls on the group @@ -935,29 +989,32 @@ ItemImpl::updateSSGroup(Db& db, CSSM_DB_RECORDTYPE recordType, CssmDataContainer // We have to reset this after we add the integrity, since it needs the attributes mDbAttributes.reset(NULL); - transaction.success(); + transaction.commit(); } catch (CssmError cssme) { const char* errStr = cssmErrorString(cssme.error); - secdebugfunc("integrity", "caught CssmError during add: %d %s", (int) cssme.error, errStr); - newSSGroup->deleteKey(nullCred); + secnotice("integrity", "caught CssmError during add: %d %s", (int) cssme.error, errStr); + + // Delete the new SSGroup that we just created + deleteSSGroup(newSSGroup, nullCred); throw; } catch (MacOSError mose) { - secdebugfunc("integrity", "caught MacOSError during add: %d", (int) mose.osStatus()); - newSSGroup->deleteKey(nullCred); + secnotice("integrity", "caught MacOSError during add: %d", (int) mose.osStatus()); + + deleteSSGroup(newSSGroup, nullCred); throw; } catch (...) { - secdebugfunc("integrity", "caught unknown exception during add"); - // Delete the new SSGroup that we just created - newSSGroup->deleteKey(nullCred); + secnotice("integrity", "caught unknown exception during add"); + + deleteSSGroup(newSSGroup, nullCred); throw; } } else { // Modify the old SSGroup - secdebugfunc("integrity", "modifying the existing SSGroup"); + secinfo("integrity", "modifying the existing SSGroup"); try { doChange(keychain, recordType, ^{ @@ -976,26 +1033,39 @@ ItemImpl::updateSSGroup(Db& db, CSSM_DB_RECORDTYPE recordType, CssmDataContainer // We have to reset this after we add the integrity, since it needs the attributes mDbAttributes.reset(NULL); - transaction.success(); + transaction.commit(); } catch (CssmError cssme) { const char* errStr = cssmErrorString(cssme.error); - secdebugfunc("integrity", "caught CssmError during modify: %d %s", (int) cssme.error, errStr); + secnotice("integrity", "caught CssmError during modify: %d %s", (int) cssme.error, errStr); throw; } catch (MacOSError mose) { - secdebugfunc("integrity", "caught MacOSError during modify: %d", (int) mose.osStatus()); + secnotice("integrity", "caught MacOSError during modify: %d", (int) mose.osStatus()); throw; } catch (...) { - secdebugfunc("integrity", "caught unknown exception during modify"); + secnotice("integrity", "caught unknown exception during modify"); throw; } } } +// Helper function to delete a group and swallow all errors +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) ()) { @@ -1007,16 +1077,16 @@ ItemImpl::doChange(Keychain keychain, CSSM_DB_RECORDTYPE recordType, void (^tryC // Try to extract the item and check its attributes, then try again if necessary auto_ptr primaryKeyAttrs; if(cssme.error == CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA) { - secdebugfunc("integrity", "possible duplicate, trying to delete invalid items"); + secnotice("integrity", "possible duplicate, trying to delete invalid items"); Keychain kc = (keychain ? keychain : mKeychain); if(!kc) { - secdebugfunc("integrity", "no valid keychain"); + secnotice("integrity", "no valid keychain"); } // Only check for corrupt items if the keychain supports them if((!kc) || !kc->hasIntegrityProtection()) { - secdebugfunc("integrity", "skipping integrity check for corrupt items due to keychain support"); + secinfo("integrity", "skipping integrity check for corrupt items due to keychain support"); throw; } else { primaryKeyAttrs.reset(getCurrentAttributes()); @@ -1037,7 +1107,7 @@ ItemImpl::doChange(Keychain keychain, CSSM_DB_RECORDTYPE recordType, void (^tryC // Our keychain doesn't know about any item with this primary key, so maybe // we have a corrupt item in the database. Let's check. - secdebugfunc("integrity", "making a cursor from primary key"); + secinfo("integrity", "making a cursor from primary key"); CssmClient::DbCursor cursor = pk->createCursor(kc); DbUniqueRecord uniqueId; @@ -1051,11 +1121,11 @@ ItemImpl::doChange(Keychain keychain, CSSM_DB_RECORDTYPE recordType, void (^tryC // Occasionally this cursor won't return the item attributes (for an unknown reason). // However, we know the attributes any item with this primary key should have, so use those instead. while (cursor->next(dbDupAttributes.get(), NULL, uniqueId)) { - secdebugfunc("integrity", "got an item..."); + secinfo("integrity", "got an item..."); SSGroup group = safer_cast(*uniqueId).group(); if(!ItemImpl::checkIntegrityFromDictionary(*group, dbDupAttributes.get())) { - secdebugfunc("integrity", "item is invalid! deleting..."); + secnotice("integrity", "item is invalid! deleting..."); uniqueId->deleteRecord(); tryAgain = true; } @@ -1063,11 +1133,11 @@ ItemImpl::doChange(Keychain keychain, CSSM_DB_RECORDTYPE recordType, void (^tryC } if(tryAgain) { - secdebugfunc("integrity", "trying again..."); + secnotice("integrity", "trying again..."); tryChange(); } else { // We didn't find an invalid item, the duplicate item exception is real - secdebugfunc("integrity", "duplicate item exception is real; throwing it on"); + secnotice("integrity", "duplicate item exception is real; throwing it on"); throw; } } @@ -1152,6 +1222,16 @@ ItemImpl::dbUniqueRecord() MacOSError::throwMe(errSecInvalidItemRef); } + // Check that our Db still matches our keychain's db. If not, find this item again in the new Db. + // Why silly !(x == y) construction? Compiler calls operator bool() on each pointer otherwise. + 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; } @@ -1232,6 +1312,8 @@ void ItemImpl::modifyContent(const SecKeychainAttributeList *attrList, UInt32 dataLength, const void *inData) { StLock_(mMutex); + StMaybeLock__ (mKeychain == NULL ? NULL : mKeychain->getKeychainMutex()); + if (!mDbAttributes.get()) { mDbAttributes.reset(new DbAttributes()); @@ -1289,7 +1371,7 @@ ItemImpl::getContent(SecItemClass *itemClass, SecKeychainAttributeList *attrList UInt32 attrCount = attrList ? attrList->count : 0; // make a DBAttributes structure and populate it - DbAttributes dbAttributes(mUniqueId->database(), attrCount); + DbAttributes dbAttributes(dbUniqueRecord()->database(), attrCount); for (UInt32 ix = 0; ix < attrCount; ++ix) { dbAttributes.add(Schema::attributeInfo(attrList->attr[ix].tag)); @@ -1338,7 +1420,7 @@ ItemImpl::getContent(SecItemClass *itemClass, SecKeychainAttributeList *attrList #if SENDACCESSNOTIFICATIONS if (outData) { - secdebug("kcnotify", "ItemImpl::getContent(%p, %p, %p, %p) retrieved content", + secinfo("kcnotify", "ItemImpl::getContent(%p, %p, %p, %p) retrieved content", itemClass, attrList, length, outData); KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this); @@ -1431,7 +1513,7 @@ ItemImpl::getAttributesAndData(SecKeychainAttributeInfo *info, SecItemClass *ite dbUniqueRecord(); UInt32 attrCount = info ? info->count : 0; - DbAttributes dbAttributes(mUniqueId->database(), attrCount); + DbAttributes dbAttributes(dbUniqueRecord()->database(), attrCount); for (UInt32 ix = 0; ix < attrCount; ix++) { CssmDbAttributeData &record = dbAttributes.add(); @@ -1452,29 +1534,35 @@ ItemImpl::getAttributesAndData(SecKeychainAttributeInfo *info, SecItemClass *ite if (info && attrList) { SecKeychainAttributeList *theList=reinterpret_cast(malloc(sizeof(SecKeychainAttributeList))); - SecKeychainAttribute *attr=reinterpret_cast(malloc(sizeof(SecKeychainAttribute)*attrCount)); - theList->count=attrCount; - theList->attr=attr; - for (UInt32 ix = 0; ix < attrCount; ++ix) - { - attr[ix].tag=info->tag[ix]; + if(attrCount == 0) { + theList->count = 0; + theList->attr = NULL; + } else { + SecKeychainAttribute *attr=reinterpret_cast(malloc(sizeof(SecKeychainAttribute)*attrCount)); + theList->count=attrCount; + theList->attr=attr; - 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; + for (UInt32 ix = 0; ix < attrCount; ++ix) + { + attr[ix].tag=info->tag[ix]; - // We don't want the data released, it is up the client - dbAttributes.at(ix).Value[0].Data = NULL; - dbAttributes.at(ix).Value[0].Length = 0; - } - else - { - attr[ix].data = NULL; - attr[ix].length = 0; - } - } + 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; + + // We don't want the data released, it is up the client + 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; } @@ -1487,7 +1575,7 @@ ItemImpl::getAttributesAndData(SecKeychainAttributeInfo *info, SecItemClass *ite itemData.Length=0; #if SENDACCESSNOTIFICATIONS - secdebug("kcnotify", "ItemImpl::getAttributesAndData(%p, %p, %p, %p, %p) retrieved data", + secinfo("kcnotify", "ItemImpl::getAttributesAndData(%p, %p, %p, %p, %p) retrieved data", info, itemClass, attrList, length, outData); KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this); @@ -1535,10 +1623,9 @@ ItemImpl::getAttribute(SecKeychainAttribute& attr, UInt32 *actualLength) if (!mKeychain) MacOSError::throwMe(errSecNoSuchAttr); - dbUniqueRecord(); - DbAttributes dbAttributes(mUniqueId->database(), 1); + DbAttributes dbAttributes(dbUniqueRecord()->database(), 1); dbAttributes.add(Schema::attributeInfo(attr.tag)); - mUniqueId->get(&dbAttributes, NULL); + dbUniqueRecord()->get(&dbAttributes, NULL); getAttributeFrom(&dbAttributes.at(0), attr, actualLength); } @@ -1568,7 +1655,7 @@ ItemImpl::getAttributeFrom(CssmDbAttributeData *data, SecKeychainAttribute &attr length = sizeof(zero); buf = &zero; } - else if (CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE) + else if (data->format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE) length = 0; // Should we throw here? else // All other formats length = 0; @@ -1655,7 +1742,7 @@ ItemImpl::getData(CssmDataContainer& outData) getContent(NULL, &outData); #if SENDACCESSNOTIFICATIONS - secdebug("kcnotify", "ItemImpl::getData retrieved data"); + secinfo("kcnotify", "ItemImpl::getData retrieved data"); //%%% be done elsewhere, but here is good for now KCEventNotifier::PostKeychainEvent(kSecDataAccessEvent, mKeychain, this); @@ -1672,7 +1759,7 @@ ItemImpl::group() Db db(mKeychain->database()); if (useSecureStorage(db)) { - group = safer_cast(*mUniqueId).group(); + group = safer_cast(*dbUniqueRecord()).group(); } } @@ -1729,19 +1816,27 @@ void ItemImpl::getContent(DbAttributes *dbAttributes, CssmDataContainer *itemData) { StLock_(mMutex); - // Make sure mUniqueId is set. - dbUniqueRecord(); if (itemData) { - Db db(mUniqueId->database()); + Db db(dbUniqueRecord()->database()); if (mDoNotEncrypt) { - mUniqueId->getWithoutEncryption (dbAttributes, itemData); + dbUniqueRecord()->getWithoutEncryption (dbAttributes, itemData); return; } if (useSecureStorage(db)) { - SSDbUniqueRecordImpl* impl = dynamic_cast(&(*mUniqueId)); + 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(&(*dbUniqueRecord())); if (impl == NULL) { CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); @@ -1754,7 +1849,7 @@ ItemImpl::getContent(DbAttributes *dbAttributes, CssmDataContainer *itemData) } } - mUniqueId->get(dbAttributes, itemData); + dbUniqueRecord()->get(dbAttributes, itemData); } bool