1/*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25//
26// KCCursor.cpp
27//
28
29#include "KCCursor.h"
30
31#include "Item.h"
32#include <security_cdsa_utilities/Schema.h>
33#include <security_cdsa_utilities/KeySchema.h>
34#include "cssmdatetime.h"
35#include "Globals.h"
36#include "StorageManager.h"
37#include <Security/SecKeychainItemPriv.h>
38#include <SecBase.h>
39
40using namespace KeychainCore;
41using namespace CssmClient;
42using namespace CSSMDateTimeUtils;
43
44using namespace KeySchema;
45
46// define a table of our attributes for easy lookup
47static const CSSM_DB_ATTRIBUTE_INFO* gKeyAttributeLookupTable[] =
48{
49	&KeyClass, &PrintName, &Alias, &Permanent, &Private, &Modifiable, &Label, &ApplicationTag, &KeyCreator,
50	&KeyType, &KeySizeInBits, &EffectiveKeySize, &StartDate, &EndDate, &Sensitive, &AlwaysSensitive, &Extractable,
51	&NeverExtractable, &Encrypt, &Decrypt, &Derive, &Sign, &Verify, &SignRecover, &VerifyRecover, &Wrap, &Unwrap
52};
53
54//
55// KCCursorImpl
56//
57KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList &searchList, SecItemClass itemClass, const SecKeychainAttributeList *attrList, CSSM_DB_CONJUNCTIVE dbConjunctive, CSSM_DB_OPERATOR dbOperator) :
58	mSearchList(searchList),
59	mCurrent(mSearchList.begin()),
60	mAllFailed(true),
61	mMutex(Mutex::recursive)
62{
63    recordType(Schema::recordTypeFor(itemClass));
64
65	if (!attrList) // No additional selectionPredicates: we are done
66		return;
67
68	conjunctive(dbConjunctive);
69	const SecKeychainAttribute *end=&attrList->attr[attrList->count];
70	// Add all the attrs in attrs list to the cursor.
71	for (const SecKeychainAttribute *attr=attrList->attr; attr != end; ++attr)
72	{
73		const CSSM_DB_ATTRIBUTE_INFO *temp;
74
75		if (attr->tag <'    ') // ok, is this a key schema?  Handle differently, just because we can...
76		{
77			temp = gKeyAttributeLookupTable[attr->tag];
78		}
79		else
80		{
81			temp = &Schema::attributeInfo(attr->tag);
82		}
83        const CssmDbAttributeInfo &info = *temp;
84        void *buf = attr->data;
85        UInt32 length = attr->length;
86        uint8 timeString[16];
87
88        // XXX This code is duplicated in NewItemImpl::setAttribute()
89        // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
90        // style attribute value.
91        if (info.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
92        {
93            if (length == sizeof(UInt32))
94            {
95                MacSecondsToTimeString(*reinterpret_cast<const UInt32 *>(buf),
96                                        16, &timeString);
97                buf = &timeString;
98                length = 16;
99            }
100            else if (length == sizeof(SInt64))
101            {
102                MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 *>(buf),
103                                            16, &timeString);
104                buf = &timeString;
105                length = 16;
106            }
107        }
108        add(dbOperator ,info, CssmData(buf,length));
109	}
110}
111
112KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList &searchList, const SecKeychainAttributeList *attrList) :
113	mSearchList(searchList),
114	mCurrent(mSearchList.begin()),
115	mAllFailed(true),
116	mMutex(Mutex::recursive)
117{
118	if (!attrList) // No additional selectionPredicates: we are done
119		return;
120
121	conjunctive(CSSM_DB_AND);
122	bool foundClassAttribute=false;
123	const SecKeychainAttribute *end=&attrList->attr[attrList->count];
124	// Add all the attrs in attrs list to the cursor.
125	for (const SecKeychainAttribute *attr=attrList->attr; attr != end; ++attr)
126	{
127		if (attr->tag!=kSecClassItemAttr)	// a regular attribute
128		{
129            const CssmDbAttributeInfo &info = Schema::attributeInfo(attr->tag);
130            void *buf = attr->data;
131            UInt32 length = attr->length;
132            uint8 timeString[16];
133
134            // XXX This code is duplicated in NewItemImpl::setAttribute()
135            // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
136            // style attribute value.
137            if (info.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
138            {
139                if (length == sizeof(UInt32))
140                {
141                    MacSecondsToTimeString(*reinterpret_cast<const UInt32 *>(buf),
142                                           16, &timeString);
143                    buf = &timeString;
144                    length = 16;
145                }
146                else if (length == sizeof(SInt64))
147                {
148                    MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 *>(buf),
149                                                16, &timeString);
150                    buf = &timeString;
151                    length = 16;
152                }
153            }
154			add(CSSM_DB_EQUAL,info, CssmData(buf,length));
155
156			continue;
157		}
158
159		// the class attribute
160		if (foundClassAttribute || attr->length != sizeof(SecItemClass))
161			MacOSError::throwMe(errSecParam); // We have 2 different 'clas' attributes
162
163		recordType(Schema::recordTypeFor(*reinterpret_cast<SecItemClass *>(attr->data)));
164		foundClassAttribute=true;
165	}
166}
167
168KCCursorImpl::~KCCursorImpl() throw()
169{
170}
171
172//static ModuleNexus<Mutex> gActivationMutex;
173
174bool
175KCCursorImpl::next(Item &item)
176{
177	StLock<Mutex>_(mMutex);
178	DbAttributes dbAttributes;
179	DbUniqueRecord uniqueId;
180	OSStatus status = 0;
181
182	for (;;)
183	{
184		while (!mDbCursor)
185		{
186			if (mCurrent == mSearchList.end())
187			{
188				// If we got always failed when calling mDbCursor->next return the error from
189				// the last call to mDbCursor->next now
190				if (mAllFailed && status)
191					CssmError::throwMe(status);
192
193				// No more keychains to search so we are done.
194				return false;
195			}
196
197			try
198			{
199                // StLock<Mutex> _(gActivationMutex()); // force serialization of cursor creation
200                Keychain &kc = *mCurrent;
201                Mutex* mutex = kc->getKeychainMutex();
202                StLock<Mutex> _(*mutex);
203				(*mCurrent)->database()->activate();
204				mDbCursor = DbCursor((*mCurrent)->database(), *this);
205			}
206			catch(const CommonError &err)
207			{
208				++mCurrent;
209			}
210		}
211
212        Keychain &kc = *mCurrent;
213        Mutex* mutex = kc->getKeychainMutex();
214        StLock<Mutex> _(*mutex);
215
216		bool gotRecord;
217		try
218		{
219			// Clear out existing attributes first!
220			// (the previous iteration may have left attributes from a different schema)
221			dbAttributes.clear();
222
223			gotRecord = mDbCursor->next(&dbAttributes, NULL, uniqueId);
224			mAllFailed = false;
225		}
226		catch(const CommonError &err)
227		{
228			// Catch the last error we get and move on to the next keychain
229			// This error will be returned when we reach the end of our keychain list
230			// iff all calls to KCCursorImpl::next failed
231			status = err.osStatus();
232			gotRecord = false;
233            dbAttributes.invalidate();
234		}
235		catch(...)
236		{
237			// Catch all other errors
238			status = errSecItemNotFound;
239			gotRecord = false;
240		}
241
242		// If we did not get a record from the current keychain or the current
243		// keychain did not exist skip to the next keychain in the list.
244		if (!gotRecord)
245		{
246			++mCurrent;
247			mDbCursor = DbCursor();
248			continue;
249		}
250
251        // If doing a search for all records, skip the db blob added by the CSPDL
252        if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_METADATA &&
253            mDbCursor->recordType() == CSSM_DL_DB_RECORD_ANY)
254                continue;
255
256        // Filter out group keys at this layer
257        if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
258        {
259			bool groupKey = false;
260			try
261			{
262				// fetch the key label attribute, if it exists
263				dbAttributes.add(KeySchema::Label);
264				Db db((*mCurrent)->database());
265				CSSM_RETURN getattr_result = CSSM_DL_DataGetFromUniqueRecordId(db->handle(), uniqueId, &dbAttributes, NULL);
266				if (getattr_result == CSSM_OK)
267				{
268					CssmDbAttributeData *label = dbAttributes.find(KeySchema::Label);
269					CssmData attrData;
270					if (label)
271						attrData = *label;
272					if (attrData.length() > 4 && !memcmp(attrData.data(), "ssgp", 4))
273						groupKey = true;
274				}
275                else
276                {
277                    dbAttributes.invalidate();
278                }
279			}
280			catch (...) {}
281
282			if (groupKey)
283				continue;
284        }
285
286		break;
287	}
288
289	// Go though Keychain since item might already exist.
290    Keychain &kc = *mCurrent;
291    StLock<Mutex> _mutexLocker(*kc->getKeychainMutex());
292	item = (*mCurrent)->item(dbAttributes.recordType(), uniqueId);
293	return true;
294}
295
296
297
298bool KCCursorImpl::mayDelete()
299{
300    if (mDbCursor.get() != NULL)
301    {
302        return mDbCursor->isIdle();
303    }
304    else
305    {
306        return true;
307    }
308}
309