X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_keychain/lib/KCCursor.cpp?ds=inline diff --git a/Security/libsecurity_keychain/lib/KCCursor.cpp b/Security/libsecurity_keychain/lib/KCCursor.cpp new file mode 100644 index 00000000..fc968cfc --- /dev/null +++ b/Security/libsecurity_keychain/lib/KCCursor.cpp @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// +// KCCursor.cpp +// + +#include "KCCursor.h" + +#include "Item.h" +#include +#include +#include "cssmdatetime.h" +#include "Globals.h" +#include "StorageManager.h" +#include +#include + +using namespace KeychainCore; +using namespace CssmClient; +using namespace CSSMDateTimeUtils; + +using namespace KeySchema; + +// define a table of our attributes for easy lookup +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::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) // No additional selectionPredicates: we are done + return; + + conjunctive(dbConjunctive); + const SecKeychainAttribute *end=&attrList->attr[attrList->count]; + // Add all the attrs in attrs list to the cursor. + for (const SecKeychainAttribute *attr=attrList->attr; attr != end; ++attr) + { + const CSSM_DB_ATTRIBUTE_INFO *temp; + + if (attr->tag <' ') // ok, is this a key schema? Handle differently, just because we can... + { + 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]; + + // XXX This code is duplicated in NewItemImpl::setAttribute() + // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE + // style attribute value. + if (info.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE) + { + if (length == sizeof(UInt32)) + { + MacSecondsToTimeString(*reinterpret_cast(buf), + 16, &timeString); + buf = &timeString; + length = 16; + } + else if (length == sizeof(SInt64)) + { + MacLongDateTimeToTimeString(*reinterpret_cast(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) // No additional selectionPredicates: we are done + return; + + conjunctive(CSSM_DB_AND); + bool foundClassAttribute=false; + const SecKeychainAttribute *end=&attrList->attr[attrList->count]; + // Add all the attrs in attrs list to the cursor. + for (const SecKeychainAttribute *attr=attrList->attr; attr != end; ++attr) + { + if (attr->tag!=kSecClassItemAttr) // a regular attribute + { + const CssmDbAttributeInfo &info = Schema::attributeInfo(attr->tag); + void *buf = attr->data; + UInt32 length = attr->length; + uint8 timeString[16]; + + // XXX This code is duplicated in NewItemImpl::setAttribute() + // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE + // style attribute value. + if (info.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE) + { + if (length == sizeof(UInt32)) + { + MacSecondsToTimeString(*reinterpret_cast(buf), + 16, &timeString); + buf = &timeString; + length = 16; + } + else if (length == sizeof(SInt64)) + { + MacLongDateTimeToTimeString(*reinterpret_cast(buf), + 16, &timeString); + buf = &timeString; + length = 16; + } + } + add(CSSM_DB_EQUAL,info, CssmData(buf,length)); + + continue; + } + + // the class attribute + if (foundClassAttribute || attr->length != sizeof(SecItemClass)) + MacOSError::throwMe(errSecParam); // We have 2 different 'clas' attributes + + recordType(Schema::recordTypeFor(*reinterpret_cast(attr->data))); + foundClassAttribute=true; + } +} + +KCCursorImpl::~KCCursorImpl() throw() +{ +} + +//static ModuleNexus gActivationMutex; + +bool +KCCursorImpl::next(Item &item) +{ + StLock_(mMutex); + DbAttributes dbAttributes; + DbUniqueRecord uniqueId; + OSStatus status = 0; + + for (;;) + { + while (!mDbCursor) + { + if (mCurrent == mSearchList.end()) + { + // If we got always failed when calling mDbCursor->next return the error from + // the last call to mDbCursor->next now + if (mAllFailed && status) + CssmError::throwMe(status); + + // No more keychains to search so we are done. + return false; + } + + try + { + // StLock _(gActivationMutex()); // force serialization of cursor creation + Keychain &kc = *mCurrent; + Mutex* mutex = kc->getKeychainMutex(); + StLock _(*mutex); + (*mCurrent)->database()->activate(); + mDbCursor = DbCursor((*mCurrent)->database(), *this); + } + catch(const CommonError &err) + { + ++mCurrent; + } + } + + Keychain &kc = *mCurrent; + Mutex* mutex = kc->getKeychainMutex(); + StLock _(*mutex); + + bool gotRecord; + try + { + // Clear out existing attributes first! + // (the previous iteration may have left attributes from a different schema) + dbAttributes.clear(); + + gotRecord = mDbCursor->next(&dbAttributes, NULL, uniqueId); + mAllFailed = false; + } + catch(const CommonError &err) + { + // Catch the last error we get and move on to the next keychain + // This error will be returned when we reach the end of our keychain list + // iff all calls to KCCursorImpl::next failed + status = err.osStatus(); + gotRecord = false; + dbAttributes.invalidate(); + } + catch(...) + { + // Catch all other errors + status = errSecItemNotFound; + gotRecord = false; + } + + // If we did not get a record from the current keychain or the current + // keychain did not exist skip to the next keychain in the list. + if (!gotRecord) + { + ++mCurrent; + mDbCursor = DbCursor(); + continue; + } + + // If doing a search for all records, skip the db blob added by the CSPDL + if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_METADATA && + mDbCursor->recordType() == CSSM_DL_DB_RECORD_ANY) + continue; + + // Filter out group keys at this layer + if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY) + { + bool groupKey = false; + try + { + // fetch the key label attribute, if it exists + 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; + } + + // Go though Keychain since item might already exist. + Keychain &kc = *mCurrent; + StLock _mutexLocker(*kc->getKeychainMutex()); + item = (*mCurrent)->item(dbAttributes.recordType(), uniqueId); + return true; +} + + + +bool KCCursorImpl::mayDelete() +{ + if (mDbCursor.get() != NULL) + { + return mDbCursor->isIdle(); + } + else + { + return true; + } +}