2 * Copyright (c) 2000-2004 Apple Computer, 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 <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
38 #include <Security/SecKeychainItemPriv.h>
40 using namespace KeychainCore
;
41 using namespace CssmClient
;
42 using namespace CSSMDateTimeUtils
;
44 using namespace KeySchema
;
46 // define a table of our attributes for easy lookup
47 static const CSSM_DB_ATTRIBUTE_INFO
* gKeyAttributeLookupTable
[] =
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
57 KCCursorImpl::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()),
61 mMutex(Mutex::recursive
)
63 recordType(Schema::recordTypeFor(itemClass
));
65 if (!attrList
) // No additional selectionPredicates: we are done
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
)
73 const CSSM_DB_ATTRIBUTE_INFO
*temp
;
75 if (attr
->tag
<' ') // ok, is this a key schema? Handle differently, just because we can...
77 temp
= gKeyAttributeLookupTable
[attr
->tag
];
81 temp
= &Schema::attributeInfo(attr
->tag
);
83 const CssmDbAttributeInfo
&info
= *temp
;
84 void *buf
= attr
->data
;
85 UInt32 length
= attr
->length
;
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
)
93 if (length
== sizeof(UInt32
))
95 MacSecondsToTimeString(*reinterpret_cast<const UInt32
*>(buf
),
100 else if (length
== sizeof(SInt64
))
102 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64
*>(buf
),
108 add(dbOperator
,info
, CssmData(buf
,length
));
112 KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList
&searchList
, const SecKeychainAttributeList
*attrList
) :
113 mSearchList(searchList
),
114 mCurrent(mSearchList
.begin()),
116 mMutex(Mutex::recursive
)
118 if (!attrList
) // No additional selectionPredicates: we are done
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
)
127 if (attr
->tag
!=kSecClassItemAttr
) // a regular attribute
129 const CssmDbAttributeInfo
&info
= Schema::attributeInfo(attr
->tag
);
130 void *buf
= attr
->data
;
131 UInt32 length
= attr
->length
;
132 uint8 timeString
[16];
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
)
139 if (length
== sizeof(UInt32
))
141 MacSecondsToTimeString(*reinterpret_cast<const UInt32
*>(buf
),
146 else if (length
== sizeof(SInt64
))
148 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64
*>(buf
),
154 add(CSSM_DB_EQUAL
,info
, CssmData(buf
,length
));
159 // the class attribute
160 if (foundClassAttribute
|| attr
->length
!= sizeof(SecItemClass
))
161 MacOSError::throwMe(paramErr
); // We have 2 different 'clas' attributes
163 recordType(Schema::recordTypeFor(*reinterpret_cast<SecItemClass
*>(attr
->data
)));
164 foundClassAttribute
=true;
168 KCCursorImpl::~KCCursorImpl() throw()
172 static ModuleNexus
<Mutex
> gActivationMutex
;
175 KCCursorImpl::next(Item
&item
)
177 StLock
<Mutex
>_(mMutex
);
178 DbAttributes dbAttributes
;
179 DbUniqueRecord uniqueId
;
186 if (mCurrent
== mSearchList
.end())
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
);
193 // No more keychains to search so we are done.
199 /* WARNING: THIS SECTION IS CRITICAL.
200 If multiple threads are attempting to activate
201 the same database at the same time, the global
202 system database queue will reflect the most recent
203 activation rather than the activation which was
204 created by this thread.
206 We could protect the database structures, but that's not the
207 right answer because it's perfectly legal to have
208 more than one context. That would basically just
209 make a big lock around CDSA, which is a performance
210 nightmare. Instead, we protect the PROCESS under
211 which databases are activated.
214 StLock
<Mutex
> _(gActivationMutex()); // force serialization of cursor creation
215 (*mCurrent
)->database()->activate();
216 mDbCursor
= DbCursor((*mCurrent
)->database(), *this);
218 catch(const CommonError
&err
)
227 // Clear out existing attributes first!
228 // (the previous iteration may have left attributes from a different schema)
229 dbAttributes
.clear();
231 gotRecord
= mDbCursor
->next(&dbAttributes
, NULL
, uniqueId
);
234 catch(const CommonError
&err
)
236 // Catch the last error we get and move on to the next keychain
237 // This error will be returned when we reach the end of our keychain list
238 // iff all calls to KCCursorImpl::next failed
239 status
= err
.osStatus();
241 dbAttributes
.invalidate();
245 // Catch all other errors
246 status
= errSecItemNotFound
;
250 // If we did not get a record from the current keychain or the current
251 // keychain did not exist skip to the next keychain in the list.
255 mDbCursor
= DbCursor();
259 // If doing a search for all records, skip the db blob added by the CSPDL
260 if (dbAttributes
.recordType() == CSSM_DL_DB_RECORD_METADATA
&&
261 mDbCursor
->recordType() == CSSM_DL_DB_RECORD_ANY
)
264 // Filter out group keys at this layer
265 if (dbAttributes
.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY
)
267 bool groupKey
= false;
270 // fetch the key label attribute, if it exists
271 dbAttributes
.add(KeySchema::Label
);
272 Db
db((*mCurrent
)->database());
273 CSSM_RETURN getattr_result
= CSSM_DL_DataGetFromUniqueRecordId(db
->handle(), uniqueId
, &dbAttributes
, NULL
);
274 if (getattr_result
== CSSM_OK
)
276 CssmDbAttributeData
*label
= dbAttributes
.find(KeySchema::Label
);
280 if (attrData
.length() > 4 && !memcmp(attrData
.data(), "ssgp", 4))
285 dbAttributes
.invalidate();
297 // Go though Keychain since item might already exist.
298 item
= (*mCurrent
)->item(dbAttributes
.recordType(), uniqueId
);