--- /dev/null
+/*
+ * 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 <security_cdsa_utilities/Schema.h>
+#include <security_cdsa_utilities/KeySchema.h>
+#include "cssmdatetime.h"
+#include "Globals.h"
+#include "StorageManager.h"
+#include <Security/SecKeychainItemPriv.h>
+#include <SecBase.h>
+
+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<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) // 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<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;
+ }
+
+ // the class attribute
+ if (foundClassAttribute || attr->length != sizeof(SecItemClass))
+ MacOSError::throwMe(errSecParam); // We have 2 different 'clas' attributes
+
+ 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 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<Mutex> _(gActivationMutex()); // force serialization of cursor creation
+ Keychain &kc = *mCurrent;
+ Mutex* mutex = kc->getKeychainMutex();
+ StLock<Mutex> _(*mutex);
+ (*mCurrent)->database()->activate();
+ mDbCursor = DbCursor((*mCurrent)->database(), *this);
+ }
+ catch(const CommonError &err)
+ {
+ ++mCurrent;
+ }
+ }
+
+ Keychain &kc = *mCurrent;
+ Mutex* mutex = kc->getKeychainMutex();
+ StLock<Mutex> _(*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<Mutex> _mutexLocker(*kc->getKeychainMutex());
+ item = (*mCurrent)->item(dbAttributes.recordType(), uniqueId);
+ return true;
+}
+
+
+
+bool KCCursorImpl::mayDelete()
+{
+ if (mDbCursor.get() != NULL)
+ {
+ return mDbCursor->isIdle();
+ }
+ else
+ {
+ return true;
+ }
+}