2 * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
32 #include <security_cdsa_utilities/Schema.h>
33 #include <security_cdsa_utilities/KeySchema.h>
34 #include "cssmdatetime.h"
36 #include "StorageManager.h"
37 #include <Security/SecKeychainItemPriv.h>
39 #include <Security/SecBasePriv.h>
41 using namespace KeychainCore
;
42 using namespace CssmClient
;
43 using namespace CSSMDateTimeUtils
;
45 using namespace KeySchema
;
47 // define a table of our attributes for easy lookup
48 static const CSSM_DB_ATTRIBUTE_INFO
* gKeyAttributeLookupTable
[] =
50 &KeyClass
, &PrintName
, &Alias
, &Permanent
, &Private
, &Modifiable
, &Label
, &ApplicationTag
, &KeyCreator
,
51 &KeyType
, &KeySizeInBits
, &EffectiveKeySize
, &StartDate
, &EndDate
, &Sensitive
, &AlwaysSensitive
, &Extractable
,
52 &NeverExtractable
, &Encrypt
, &Decrypt
, &Derive
, &Sign
, &Verify
, &SignRecover
, &VerifyRecover
, &Wrap
, &Unwrap
58 KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList
&searchList
, SecItemClass itemClass
, const SecKeychainAttributeList
*attrList
, CSSM_DB_CONJUNCTIVE dbConjunctive
, CSSM_DB_OPERATOR dbOperator
) :
59 mSearchList(searchList
),
60 mCurrent(mSearchList
.begin()),
62 mDeleteInvalidRecords(false),
63 mMutex(Mutex::recursive
),
64 mKeychainReadLock(NULL
)
66 recordType(Schema::recordTypeFor(itemClass
));
68 if (!attrList
) // No additional selectionPredicates: we are done
71 conjunctive(dbConjunctive
);
72 const SecKeychainAttribute
*end
=&attrList
->attr
[attrList
->count
];
73 // Add all the attrs in attrs list to the cursor.
74 for (const SecKeychainAttribute
*attr
=attrList
->attr
; attr
!= end
; ++attr
)
76 const CSSM_DB_ATTRIBUTE_INFO
*temp
;
78 if (attr
->tag
<' ') // ok, is this a key schema? Handle differently, just because we can...
80 temp
= gKeyAttributeLookupTable
[attr
->tag
];
84 temp
= &Schema::attributeInfo(attr
->tag
);
86 const CssmDbAttributeInfo
&info
= *temp
;
87 void *buf
= attr
->data
;
88 UInt32 length
= attr
->length
;
91 // XXX This code is duplicated in NewItemImpl::setAttribute()
92 // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
93 // style attribute value.
94 if (info
.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
)
96 if (length
== sizeof(UInt32
))
98 MacSecondsToTimeString(*reinterpret_cast<const UInt32
*>(buf
),
103 else if (length
== sizeof(SInt64
))
105 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64
*>(buf
),
111 add(dbOperator
,info
, CssmData(buf
,length
));
115 KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList
&searchList
, const SecKeychainAttributeList
*attrList
) :
116 mSearchList(searchList
),
117 mCurrent(mSearchList
.begin()),
119 mDeleteInvalidRecords(false),
120 mMutex(Mutex::recursive
),
121 mKeychainReadLock(NULL
)
123 if (!attrList
) // No additional selectionPredicates: we are done
126 conjunctive(CSSM_DB_AND
);
127 bool foundClassAttribute
=false;
128 const SecKeychainAttribute
*end
=&attrList
->attr
[attrList
->count
];
129 // Add all the attrs in attrs list to the cursor.
130 for (const SecKeychainAttribute
*attr
=attrList
->attr
; attr
!= end
; ++attr
)
132 if (attr
->tag
!=kSecClassItemAttr
) // a regular attribute
134 const CssmDbAttributeInfo
&info
= Schema::attributeInfo(attr
->tag
);
135 void *buf
= attr
->data
;
136 UInt32 length
= attr
->length
;
137 uint8 timeString
[16];
139 // XXX This code is duplicated in NewItemImpl::setAttribute()
140 // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
141 // style attribute value.
142 if (info
.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
)
144 if (length
== sizeof(UInt32
))
146 MacSecondsToTimeString(*reinterpret_cast<const UInt32
*>(buf
),
151 else if (length
== sizeof(SInt64
))
153 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64
*>(buf
),
159 add(CSSM_DB_EQUAL
,info
, CssmData(buf
,length
));
164 // the class attribute
165 if (foundClassAttribute
|| attr
->length
!= sizeof(SecItemClass
))
166 MacOSError::throwMe(errSecParam
); // We have 2 different 'clas' attributes
168 recordType(Schema::recordTypeFor(*reinterpret_cast<SecItemClass
*>(attr
->data
)));
169 foundClassAttribute
=true;
173 KCCursorImpl::~KCCursorImpl() throw()
175 if(mKeychainReadLock
) {
176 delete mKeychainReadLock
;
180 //static ModuleNexus<Mutex> gActivationMutex;
183 KCCursorImpl::next(Item
&item
)
185 StLock
<Mutex
>_(mMutex
);
186 DbAttributes dbAttributes
;
187 DbUniqueRecord uniqueId
;
190 // if this is true, we should perform newKeychain() on mCurrent before
191 // taking any locks. Starts false because mDbCursor isn't anything yet, and
192 // so the while loop will run newKeychain for us.
193 bool isNewKeychain
= false;
197 Item tempItem
= NULL
;
200 newKeychain(mCurrent
);
201 isNewKeychain
= false;
206 // Do the newKeychain dance before we check our done status
207 newKeychain(mCurrent
);
209 if (mCurrent
== mSearchList
.end())
211 // If we got always failed when calling mDbCursor->next return the error from
212 // the last call to mDbCursor->next now
213 if (mAllFailed
&& status
)
214 CssmError::throwMe(status
);
216 // No more keychains to search so we are done.
222 // StLock<Mutex> _(gActivationMutex()); // force serialization of cursor creation
223 Keychain
&kc
= *mCurrent
;
224 Mutex
* mutex
= kc
->getKeychainMutex();
225 StLock
<Mutex
> _(*mutex
);
226 (*mCurrent
)->database()->activate();
227 mDbCursor
= DbCursor((*mCurrent
)->database(), *this);
229 catch(const CommonError
&err
)
235 Keychain
&kc
= *mCurrent
;
236 Mutex
* mutex
= kc
->getKeychainMutex();
237 StLock
<Mutex
> _(*mutex
);
242 // Clear out existing attributes first!
243 // (the previous iteration may have left attributes from a different schema)
244 dbAttributes
.clear();
246 gotRecord
= mDbCursor
->next(&dbAttributes
, NULL
, uniqueId
);
249 catch(const CommonError
&err
)
251 // Catch the last error we get and move on to the next keychain
252 // This error will be returned when we reach the end of our keychain list
253 // iff all calls to KCCursorImpl::next failed
254 status
= err
.osStatus();
256 dbAttributes
.invalidate();
260 // Catch all other errors
261 status
= errSecItemNotFound
;
265 // If we did not get a record from the current keychain or the current
266 // keychain did not exist skip to the next keychain in the list.
270 mDbCursor
= DbCursor();
271 // we'd like to call newKeychain(mCurrent) here, but to avoid deadlock
272 // we need to drop the current keychain's mutex first. Use this silly
273 // hack to void the stack Mutex object.
274 isNewKeychain
= true;
278 // If doing a search for all records, skip the db blob added by the CSPDL
279 if (dbAttributes
.recordType() == CSSM_DL_DB_RECORD_METADATA
&&
280 mDbCursor
->recordType() == CSSM_DL_DB_RECORD_ANY
)
283 // Filter out group keys at this layer
284 if (dbAttributes
.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY
)
286 bool groupKey
= false;
289 // fetch the key label attribute, if it exists
290 dbAttributes
.add(KeySchema::Label
);
291 Db
db((*mCurrent
)->database());
292 CSSM_RETURN getattr_result
= CSSM_DL_DataGetFromUniqueRecordId(db
->handle(), uniqueId
, &dbAttributes
, NULL
);
293 if (getattr_result
== CSSM_OK
)
295 CssmDbAttributeData
*label
= dbAttributes
.find(KeySchema::Label
);
299 if (attrData
.length() > 4 && !memcmp(attrData
.data(), "ssgp", 4))
304 dbAttributes
.invalidate();
314 // Go though Keychain since item might already exist.
315 // This might throw a CSSMERR_DL_RECORD_NOT_FOUND or be otherwise invalid. If we're supposed to delete these items, delete them...
317 tempItem
= (*mCurrent
)->item(dbAttributes
.recordType(), uniqueId
);
318 } catch(CssmError cssme
) {
319 if (mDeleteInvalidRecords
) {
320 // This is an invalid record for some reason; delete it and restart the loop
321 const char* errStr
= cssmErrorString(cssme
.error
);
322 secdebugfunc("integrity", "deleting corrupt record because: %d %s", (int) cssme
.error
, errStr
);
324 deleteInvalidRecord(uniqueId
);
325 // if deleteInvalidRecord doesn't throw, we want to restart the loop
332 // release the Keychain lock before checking item integrity to avoid deadlock
335 // If the Item's attribute hash does not match, skip the item
336 if(!tempItem
->checkIntegrity()) {
337 secdebugfunc("integrity", "item has no integrity, skipping");
340 } catch(CssmError cssme
) {
341 if (mDeleteInvalidRecords
) {
342 // This is an invalid record for some reason; delete it and restart the loop
343 const char* errStr
= cssmErrorString(cssme
.error
);
344 secdebugfunc("integrity", "deleting corrupt record because: %d %s", (int) cssme
.error
, errStr
);
346 deleteInvalidRecord(uniqueId
);
347 // if deleteInvalidRecord doesn't throw, we want to restart the loop
359 // If we reach here, we've found an item
363 void KCCursorImpl::deleteInvalidRecord(DbUniqueRecord
& uniqueId
) {
364 // This might throw a RECORD_NOT_FOUND. Handle that, because we don't care if we're trying to delete the item.
366 uniqueId
->deleteRecord();
367 } catch(CssmError delcssme
) {
368 if (delcssme
.osStatus() == CSSMERR_DL_RECORD_NOT_FOUND
) {
369 secdebugfunc("integrity", "couldn't delete nonexistent record (this is okay)");
378 bool KCCursorImpl::mayDelete()
380 if (mDbCursor
.get() != NULL
)
382 return mDbCursor
->isIdle();
390 void KCCursorImpl::setDeleteInvalidRecords(bool deleteRecord
) {
391 mDeleteInvalidRecords
= deleteRecord
;
394 void KCCursorImpl::newKeychain(StorageManager::KeychainList::iterator kcIter
) {
395 // Always lose the last keychain's lock
396 if(mKeychainReadLock
) {
397 delete mKeychainReadLock
;
398 mKeychainReadLock
= NULL
;
401 if(kcIter
!= mSearchList
.end()) {
402 (*kcIter
)->performKeychainUpgradeIfNeeded();
404 // Grab a read lock on the keychain
405 mKeychainReadLock
= new StReadWriteLock(*((*kcIter
)->getKeychainReadWriteLock()), StReadWriteLock::Read
);