]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/KCCursor.cpp
Security-57740.1.18.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 mIsNewKeychain(true),
64 mMutex(Mutex::recursive),
65 mKeychainReadLock(NULL)
66 {
67 recordType(Schema::recordTypeFor(itemClass));
68
69 if (!attrList) // No additional selectionPredicates: we are done
70 return;
71
72 conjunctive(dbConjunctive);
73 const SecKeychainAttribute *end=&attrList->attr[attrList->count];
74 // Add all the attrs in attrs list to the cursor.
75 for (const SecKeychainAttribute *attr=attrList->attr; attr != end; ++attr)
76 {
77 const CSSM_DB_ATTRIBUTE_INFO *temp;
78
79 if (attr->tag <' ') // ok, is this a key schema? Handle differently, just because we can...
80 {
81 temp = gKeyAttributeLookupTable[attr->tag];
82 }
83 else
84 {
85 temp = &Schema::attributeInfo(attr->tag);
86 }
87 const CssmDbAttributeInfo &info = *temp;
88 void *buf = attr->data;
89 UInt32 length = attr->length;
90 uint8 timeString[16];
91
92 // XXX This code is duplicated in NewItemImpl::setAttribute()
93 // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
94 // style attribute value.
95 if (info.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
96 {
97 if (length == sizeof(UInt32))
98 {
99 MacSecondsToTimeString(*reinterpret_cast<const UInt32 *>(buf),
100 16, &timeString);
101 buf = &timeString;
102 length = 16;
103 }
104 else if (length == sizeof(SInt64))
105 {
106 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 *>(buf),
107 16, &timeString);
108 buf = &timeString;
109 length = 16;
110 }
111 }
112 add(dbOperator ,info, CssmData(buf,length));
113 }
114 }
115
116 KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList &searchList, const SecKeychainAttributeList *attrList) :
117 mSearchList(searchList),
118 mCurrent(mSearchList.begin()),
119 mAllFailed(true),
120 mDeleteInvalidRecords(false),
121 mIsNewKeychain(true),
122 mMutex(Mutex::recursive),
123 mKeychainReadLock(NULL)
124 {
125 if (!attrList) // No additional selectionPredicates: we are done
126 return;
127
128 conjunctive(CSSM_DB_AND);
129 bool foundClassAttribute=false;
130 const SecKeychainAttribute *end=&attrList->attr[attrList->count];
131 // Add all the attrs in attrs list to the cursor.
132 for (const SecKeychainAttribute *attr=attrList->attr; attr != end; ++attr)
133 {
134 if (attr->tag!=kSecClassItemAttr) // a regular attribute
135 {
136 const CssmDbAttributeInfo &info = Schema::attributeInfo(attr->tag);
137 void *buf = attr->data;
138 UInt32 length = attr->length;
139 uint8 timeString[16];
140
141 // XXX This code is duplicated in NewItemImpl::setAttribute()
142 // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
143 // style attribute value.
144 if (info.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
145 {
146 if (length == sizeof(UInt32))
147 {
148 MacSecondsToTimeString(*reinterpret_cast<const UInt32 *>(buf),
149 16, &timeString);
150 buf = &timeString;
151 length = 16;
152 }
153 else if (length == sizeof(SInt64))
154 {
155 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 *>(buf),
156 16, &timeString);
157 buf = &timeString;
158 length = 16;
159 }
160 }
161 add(CSSM_DB_EQUAL,info, CssmData(buf,length));
162
163 continue;
164 }
165
166 // the class attribute
167 if (foundClassAttribute || attr->length != sizeof(SecItemClass))
168 MacOSError::throwMe(errSecParam); // We have 2 different 'clas' attributes
169
170 recordType(Schema::recordTypeFor(*reinterpret_cast<SecItemClass *>(attr->data)));
171 foundClassAttribute=true;
172 }
173 }
174
175 KCCursorImpl::~KCCursorImpl() throw()
176 {
177 if(mKeychainReadLock) {
178 delete mKeychainReadLock;
179 }
180 }
181
182 //static ModuleNexus<Mutex> gActivationMutex;
183
184 bool
185 KCCursorImpl::next(Item &item)
186 {
187 StLock<Mutex>_(mMutex);
188 DbAttributes dbAttributes;
189 DbUniqueRecord uniqueId;
190 OSStatus status = 0;
191
192 for (;;)
193 {
194 Item tempItem = NULL;
195 {
196 while (!mDbCursor)
197 {
198 // Do the newKeychain dance before we check our done status
199 newKeychain(mCurrent);
200
201 if (mCurrent == mSearchList.end())
202 {
203 // If we got always failed when calling mDbCursor->next return the error from
204 // the last call to mDbCursor->next now
205 if (mAllFailed && status)
206 CssmError::throwMe(status);
207
208 // No more keychains to search so we are done.
209 return false;
210 }
211
212 try
213 {
214 // StLock<Mutex> _(gActivationMutex()); // force serialization of cursor creation
215 Keychain &kc = *mCurrent;
216 Mutex* mutex = kc->getKeychainMutex();
217 StLock<Mutex> _(*mutex);
218 (*mCurrent)->database()->activate();
219 mDbCursor = DbCursor((*mCurrent)->database(), *this);
220 }
221 catch(const CommonError &err)
222 {
223 ++mCurrent;
224 mIsNewKeychain = true;
225 }
226 }
227
228 Keychain &kc = *mCurrent;
229 Mutex* mutex = kc->getKeychainMutex();
230 StLock<Mutex> _(*mutex);
231
232 bool gotRecord;
233 try
234 {
235 // Clear out existing attributes first!
236 // (the previous iteration may have left attributes from a different schema)
237 dbAttributes.clear();
238
239 gotRecord = mDbCursor->next(&dbAttributes, NULL, uniqueId);
240 mAllFailed = false;
241 }
242 catch(const CommonError &err)
243 {
244 // Catch the last error we get and move on to the next keychain
245 // This error will be returned when we reach the end of our keychain list
246 // iff all calls to KCCursorImpl::next failed
247 status = err.osStatus();
248 gotRecord = false;
249 dbAttributes.invalidate();
250 }
251 catch(...)
252 {
253 // Catch all other errors
254 status = errSecItemNotFound;
255 gotRecord = false;
256 }
257
258 // If we did not get a record from the current keychain or the current
259 // keychain did not exist skip to the next keychain in the list.
260 if (!gotRecord)
261 {
262 ++mCurrent;
263 mDbCursor = DbCursor();
264 // we'd like to call newKeychain(mCurrent) here, but to avoid deadlock
265 // we need to drop the current keychain's mutex first. Use this silly
266 // hack to void the stack Mutex object.
267 mIsNewKeychain = true;
268 continue;
269 }
270
271 // If doing a search for all records, skip the db blob added by the CSPDL
272 if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_METADATA &&
273 mDbCursor->recordType() == CSSM_DL_DB_RECORD_ANY)
274 continue;
275
276 // Filter out group keys at this layer
277 if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
278 {
279 bool groupKey = false;
280 try
281 {
282 // fetch the key label attribute, if it exists
283 dbAttributes.add(KeySchema::Label);
284 Db db((*mCurrent)->database());
285 CSSM_RETURN getattr_result = CSSM_DL_DataGetFromUniqueRecordId(db->handle(), uniqueId, &dbAttributes, NULL);
286 if (getattr_result == CSSM_OK)
287 {
288 CssmDbAttributeData *label = dbAttributes.find(KeySchema::Label);
289 CssmData attrData;
290 if (label)
291 attrData = *label;
292 if (attrData.length() > 4 && !memcmp(attrData.data(), "ssgp", 4))
293 groupKey = true;
294 }
295 else
296 {
297 dbAttributes.invalidate();
298 }
299 }
300 catch (...) {}
301
302 if (groupKey)
303 continue;
304 }
305
306 // Create the Item
307 // Go though Keychain since item might already exist.
308 // This might throw a CSSMERR_DL_RECORD_NOT_FOUND or be otherwise invalid. If we're supposed to delete these items, delete them...
309 try {
310 tempItem = (*mCurrent)->item(dbAttributes.recordType(), uniqueId);
311 } catch(CssmError cssme) {
312 if (mDeleteInvalidRecords) {
313 // This is an invalid record for some reason; delete it and restart the loop
314 const char* errStr = cssmErrorString(cssme.error);
315 secnotice("integrity", "deleting corrupt record because: %d %s", (int) cssme.error, errStr);
316
317 deleteInvalidRecord(uniqueId);
318 // if deleteInvalidRecord doesn't throw, we want to restart the loop
319 continue;
320 } else {
321 throw;
322 }
323 }
324 }
325 // release the Keychain lock before checking item integrity to avoid deadlock
326
327 item = tempItem;
328
329 break;
330 }
331
332 // If we reach here, we've found an item
333 return true;
334 }
335
336 void KCCursorImpl::deleteInvalidRecord(DbUniqueRecord& uniqueId) {
337 // This might throw a RECORD_NOT_FOUND. Handle that, because we don't care if we're trying to delete the item.
338 try {
339 uniqueId->deleteRecord();
340 } catch(CssmError delcssme) {
341 if (delcssme.osStatus() == CSSMERR_DL_RECORD_NOT_FOUND) {
342 secnotice("integrity", "couldn't delete nonexistent record (this is okay)");
343 } else {
344 throw;
345 }
346 }
347 }
348
349
350
351 bool KCCursorImpl::mayDelete()
352 {
353 if (mDbCursor.get() != NULL)
354 {
355 return mDbCursor->isIdle();
356 }
357 else
358 {
359 return true;
360 }
361 }
362
363 void KCCursorImpl::setDeleteInvalidRecords(bool deleteRecord) {
364 mDeleteInvalidRecords = deleteRecord;
365 }
366
367 void KCCursorImpl::newKeychain(StorageManager::KeychainList::iterator kcIter) {
368 if(!mIsNewKeychain) {
369 // We've already been called on this keychain, don't bother.
370 return;
371 }
372
373 // Always lose the last keychain's lock
374 if(mKeychainReadLock) {
375 delete mKeychainReadLock;
376 mKeychainReadLock = NULL;
377 }
378
379 if(kcIter != mSearchList.end()) {
380 (*kcIter)->performKeychainUpgradeIfNeeded();
381 (*kcIter)->tickle();
382
383 // Grab a read lock on the keychain
384 mKeychainReadLock = new StReadWriteLock(*((*kcIter)->getKeychainReadWriteLock()), StReadWriteLock::Read);
385 }
386
387 // Mark down that this function has been called
388 mIsNewKeychain = false;
389 }