]> git.saurik.com Git - apple/security.git/blob - libsecurity_keychain/lib/KCCursor.cpp
Security-55179.13.tar.gz
[apple/security.git] / libsecurity_keychain / lib / KCCursor.cpp
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 <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
38 #include <Security/SecKeychainItemPriv.h>
39
40 using namespace KeychainCore;
41 using namespace CssmClient;
42 using namespace CSSMDateTimeUtils;
43
44 using namespace KeySchema;
45
46 // define a table of our attributes for easy lookup
47 static 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 //
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()),
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
112 KCCursorImpl::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(paramErr); // We have 2 different 'clas' attributes
162
163 recordType(Schema::recordTypeFor(*reinterpret_cast<SecItemClass *>(attr->data)));
164 foundClassAttribute=true;
165 }
166 }
167
168 KCCursorImpl::~KCCursorImpl() throw()
169 {
170 }
171
172 static ModuleNexus<Mutex> gActivationMutex;
173
174 bool
175 KCCursorImpl::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 /* 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.
205
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.
212 */
213
214 StLock<Mutex> _(gActivationMutex()); // force serialization of cursor creation
215 (*mCurrent)->database()->activate();
216 mDbCursor = DbCursor((*mCurrent)->database(), *this);
217 }
218 catch(const CommonError &err)
219 {
220 ++mCurrent;
221 }
222 }
223
224 bool gotRecord;
225 try
226 {
227 // Clear out existing attributes first!
228 // (the previous iteration may have left attributes from a different schema)
229 dbAttributes.clear();
230
231 gotRecord = mDbCursor->next(&dbAttributes, NULL, uniqueId);
232 mAllFailed = false;
233 }
234 catch(const CommonError &err)
235 {
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();
240 gotRecord = false;
241 dbAttributes.invalidate();
242 }
243 catch(...)
244 {
245 // Catch all other errors
246 status = errSecItemNotFound;
247 gotRecord = false;
248 }
249
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.
252 if (!gotRecord)
253 {
254 ++mCurrent;
255 mDbCursor = DbCursor();
256 continue;
257 }
258
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)
262 continue;
263
264 // Filter out group keys at this layer
265 if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
266 {
267 bool groupKey = false;
268 try
269 {
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)
275 {
276 CssmDbAttributeData *label = dbAttributes.find(KeySchema::Label);
277 CssmData attrData;
278 if (label)
279 attrData = *label;
280 if (attrData.length() > 4 && !memcmp(attrData.data(), "ssgp", 4))
281 groupKey = true;
282 }
283 else
284 {
285 dbAttributes.invalidate();
286 }
287 }
288 catch (...) {}
289
290 if (groupKey)
291 continue;
292 }
293
294 break;
295 }
296
297 // Go though Keychain since item might already exist.
298 item = (*mCurrent)->item(dbAttributes.recordType(), uniqueId);
299 return true;
300 }