]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/KCCursor.cpp
Security-57337.40.85.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / KCCursor.cpp
1 /*
2 * Copyright (c) 2000-2004,2011-2014 Apple 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 #include <Security/SecBasePriv.h>
40
41 using namespace KeychainCore;
42 using namespace CssmClient;
43 using namespace CSSMDateTimeUtils;
44
45 using namespace KeySchema;
46
47 // define a table of our attributes for easy lookup
48 static const CSSM_DB_ATTRIBUTE_INFO* gKeyAttributeLookupTable[] =
49 {
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
53 };
54
55 //
56 // KCCursorImpl
57 //
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()),
61 mAllFailed(true),
62 mDeleteInvalidRecords(false),
63 mMutex(Mutex::recursive),
64 mKeychainReadLock(NULL)
65 {
66 recordType(Schema::recordTypeFor(itemClass));
67
68 if (!attrList) // No additional selectionPredicates: we are done
69 return;
70
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)
75 {
76 const CSSM_DB_ATTRIBUTE_INFO *temp;
77
78 if (attr->tag <' ') // ok, is this a key schema? Handle differently, just because we can...
79 {
80 temp = gKeyAttributeLookupTable[attr->tag];
81 }
82 else
83 {
84 temp = &Schema::attributeInfo(attr->tag);
85 }
86 const CssmDbAttributeInfo &info = *temp;
87 void *buf = attr->data;
88 UInt32 length = attr->length;
89 uint8 timeString[16];
90
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)
95 {
96 if (length == sizeof(UInt32))
97 {
98 MacSecondsToTimeString(*reinterpret_cast<const UInt32 *>(buf),
99 16, &timeString);
100 buf = &timeString;
101 length = 16;
102 }
103 else if (length == sizeof(SInt64))
104 {
105 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 *>(buf),
106 16, &timeString);
107 buf = &timeString;
108 length = 16;
109 }
110 }
111 add(dbOperator ,info, CssmData(buf,length));
112 }
113 }
114
115 KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList &searchList, const SecKeychainAttributeList *attrList) :
116 mSearchList(searchList),
117 mCurrent(mSearchList.begin()),
118 mAllFailed(true),
119 mDeleteInvalidRecords(false),
120 mMutex(Mutex::recursive),
121 mKeychainReadLock(NULL)
122 {
123 if (!attrList) // No additional selectionPredicates: we are done
124 return;
125
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)
131 {
132 if (attr->tag!=kSecClassItemAttr) // a regular attribute
133 {
134 const CssmDbAttributeInfo &info = Schema::attributeInfo(attr->tag);
135 void *buf = attr->data;
136 UInt32 length = attr->length;
137 uint8 timeString[16];
138
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)
143 {
144 if (length == sizeof(UInt32))
145 {
146 MacSecondsToTimeString(*reinterpret_cast<const UInt32 *>(buf),
147 16, &timeString);
148 buf = &timeString;
149 length = 16;
150 }
151 else if (length == sizeof(SInt64))
152 {
153 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 *>(buf),
154 16, &timeString);
155 buf = &timeString;
156 length = 16;
157 }
158 }
159 add(CSSM_DB_EQUAL,info, CssmData(buf,length));
160
161 continue;
162 }
163
164 // the class attribute
165 if (foundClassAttribute || attr->length != sizeof(SecItemClass))
166 MacOSError::throwMe(errSecParam); // We have 2 different 'clas' attributes
167
168 recordType(Schema::recordTypeFor(*reinterpret_cast<SecItemClass *>(attr->data)));
169 foundClassAttribute=true;
170 }
171 }
172
173 KCCursorImpl::~KCCursorImpl() throw()
174 {
175 if(mKeychainReadLock) {
176 delete mKeychainReadLock;
177 }
178 }
179
180 //static ModuleNexus<Mutex> gActivationMutex;
181
182 bool
183 KCCursorImpl::next(Item &item)
184 {
185 StLock<Mutex>_(mMutex);
186 DbAttributes dbAttributes;
187 DbUniqueRecord uniqueId;
188 OSStatus status = 0;
189
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;
194
195 for (;;)
196 {
197 Item tempItem = NULL;
198 {
199 if(isNewKeychain) {
200 newKeychain(mCurrent);
201 isNewKeychain = false;
202 }
203
204 while (!mDbCursor)
205 {
206 // Do the newKeychain dance before we check our done status
207 newKeychain(mCurrent);
208
209 if (mCurrent == mSearchList.end())
210 {
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);
215
216 // No more keychains to search so we are done.
217 return false;
218 }
219
220 try
221 {
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);
228 }
229 catch(const CommonError &err)
230 {
231 ++mCurrent;
232 }
233 }
234
235 Keychain &kc = *mCurrent;
236 Mutex* mutex = kc->getKeychainMutex();
237 StLock<Mutex> _(*mutex);
238
239 bool gotRecord;
240 try
241 {
242 // Clear out existing attributes first!
243 // (the previous iteration may have left attributes from a different schema)
244 dbAttributes.clear();
245
246 gotRecord = mDbCursor->next(&dbAttributes, NULL, uniqueId);
247 mAllFailed = false;
248 }
249 catch(const CommonError &err)
250 {
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();
255 gotRecord = false;
256 dbAttributes.invalidate();
257 }
258 catch(...)
259 {
260 // Catch all other errors
261 status = errSecItemNotFound;
262 gotRecord = false;
263 }
264
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.
267 if (!gotRecord)
268 {
269 ++mCurrent;
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;
275 continue;
276 }
277
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)
281 continue;
282
283 // Filter out group keys at this layer
284 if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
285 {
286 bool groupKey = false;
287 try
288 {
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)
294 {
295 CssmDbAttributeData *label = dbAttributes.find(KeySchema::Label);
296 CssmData attrData;
297 if (label)
298 attrData = *label;
299 if (attrData.length() > 4 && !memcmp(attrData.data(), "ssgp", 4))
300 groupKey = true;
301 }
302 else
303 {
304 dbAttributes.invalidate();
305 }
306 }
307 catch (...) {}
308
309 if (groupKey)
310 continue;
311 }
312
313 // Create the Item
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...
316 try {
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);
323
324 deleteInvalidRecord(uniqueId);
325 // if deleteInvalidRecord doesn't throw, we want to restart the loop
326 continue;
327 } else {
328 throw;
329 }
330 }
331 }
332 // release the Keychain lock before checking item integrity to avoid deadlock
333
334 try {
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");
338 continue;
339 }
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);
345
346 deleteInvalidRecord(uniqueId);
347 // if deleteInvalidRecord doesn't throw, we want to restart the loop
348 continue;
349 } else {
350 throw;
351 }
352 }
353
354 item = tempItem;
355
356 break;
357 }
358
359 // If we reach here, we've found an item
360 return true;
361 }
362
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.
365 try {
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)");
370 } else {
371 throw;
372 }
373 }
374 }
375
376
377
378 bool KCCursorImpl::mayDelete()
379 {
380 if (mDbCursor.get() != NULL)
381 {
382 return mDbCursor->isIdle();
383 }
384 else
385 {
386 return true;
387 }
388 }
389
390 void KCCursorImpl::setDeleteInvalidRecords(bool deleteRecord) {
391 mDeleteInvalidRecords = deleteRecord;
392 }
393
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;
399 }
400
401 if(kcIter != mSearchList.end()) {
402 (*kcIter)->performKeychainUpgradeIfNeeded();
403
404 // Grab a read lock on the keychain
405 mKeychainReadLock = new StReadWriteLock(*((*kcIter)->getKeychainReadWriteLock()), StReadWriteLock::Read);
406 }
407 }