]> git.saurik.com Git - apple/security.git/blame - OSX/libsecurity_keychain/lib/KCCursor.cpp
Security-59754.80.3.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / KCCursor.cpp
CommitLineData
b1ab9ed8 1/*
d8f41ccd 2 * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved.
b1ab9ed8
A
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"
b1ab9ed8 37#include <Security/SecKeychainItemPriv.h>
b54c578e 38#include <Security/SecBase.h>
e3d460c9 39#include <Security/SecBasePriv.h>
6b200bc3 40#include <utilities/array_size.h>
b1ab9ed8
A
41
42using namespace KeychainCore;
43using namespace CssmClient;
44using namespace CSSMDateTimeUtils;
45
46using namespace KeySchema;
47
48// define a table of our attributes for easy lookup
49static const CSSM_DB_ATTRIBUTE_INFO* gKeyAttributeLookupTable[] =
50{
51 &KeyClass, &PrintName, &Alias, &Permanent, &Private, &Modifiable, &Label, &ApplicationTag, &KeyCreator,
52 &KeyType, &KeySizeInBits, &EffectiveKeySize, &StartDate, &EndDate, &Sensitive, &AlwaysSensitive, &Extractable,
53 &NeverExtractable, &Encrypt, &Decrypt, &Derive, &Sign, &Verify, &SignRecover, &VerifyRecover, &Wrap, &Unwrap
54};
55
56//
57// KCCursorImpl
58//
59KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList &searchList, SecItemClass itemClass, const SecKeychainAttributeList *attrList, CSSM_DB_CONJUNCTIVE dbConjunctive, CSSM_DB_OPERATOR dbOperator) :
60 mSearchList(searchList),
61 mCurrent(mSearchList.begin()),
62 mAllFailed(true),
e3d460c9 63 mDeleteInvalidRecords(false),
fa7225c8 64 mIsNewKeychain(true),
b04fe171 65 mMutex(Mutex::recursive)
b1ab9ed8
A
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;
6b200bc3
A
78
79 // ok, is this a key schema? Handle differently, just because we can...
80 if (attr->tag <' ' && attr->tag < array_size(gKeyAttributeLookupTable))
b1ab9ed8
A
81 {
82 temp = gKeyAttributeLookupTable[attr->tag];
83 }
84 else
85 {
86 temp = &Schema::attributeInfo(attr->tag);
87 }
88 const CssmDbAttributeInfo &info = *temp;
89 void *buf = attr->data;
90 UInt32 length = attr->length;
91 uint8 timeString[16];
92
93 // XXX This code is duplicated in NewItemImpl::setAttribute()
94 // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE
95 // style attribute value.
96 if (info.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE)
97 {
98 if (length == sizeof(UInt32))
99 {
100 MacSecondsToTimeString(*reinterpret_cast<const UInt32 *>(buf),
101 16, &timeString);
102 buf = &timeString;
103 length = 16;
104 }
105 else if (length == sizeof(SInt64))
106 {
107 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 *>(buf),
108 16, &timeString);
109 buf = &timeString;
110 length = 16;
111 }
112 }
113 add(dbOperator ,info, CssmData(buf,length));
114 }
115}
116
117KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList &searchList, const SecKeychainAttributeList *attrList) :
118 mSearchList(searchList),
119 mCurrent(mSearchList.begin()),
120 mAllFailed(true),
e3d460c9 121 mDeleteInvalidRecords(false),
fa7225c8 122 mIsNewKeychain(true),
b04fe171 123 mMutex(Mutex::recursive)
b1ab9ed8
A
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))
427c49bc 168 MacOSError::throwMe(errSecParam); // We have 2 different 'clas' attributes
b1ab9ed8
A
169
170 recordType(Schema::recordTypeFor(*reinterpret_cast<SecItemClass *>(attr->data)));
171 foundClassAttribute=true;
172 }
173}
174
d64be36e 175KCCursorImpl::~KCCursorImpl() _NOEXCEPT
b1ab9ed8
A
176{
177}
178
427c49bc 179//static ModuleNexus<Mutex> gActivationMutex;
b1ab9ed8
A
180
181bool
182KCCursorImpl::next(Item &item)
183{
184 StLock<Mutex>_(mMutex);
185 DbAttributes dbAttributes;
186 DbUniqueRecord uniqueId;
187 OSStatus status = 0;
188
189 for (;;)
190 {
e3d460c9
A
191 Item tempItem = NULL;
192 {
e3d460c9
A
193 while (!mDbCursor)
194 {
195 // Do the newKeychain dance before we check our done status
196 newKeychain(mCurrent);
b1ab9ed8 197
e3d460c9
A
198 if (mCurrent == mSearchList.end())
199 {
200 // If we got always failed when calling mDbCursor->next return the error from
201 // the last call to mDbCursor->next now
202 if (mAllFailed && status)
203 CssmError::throwMe(status);
b1ab9ed8 204
e3d460c9
A
205 // No more keychains to search so we are done.
206 return false;
207 }
208
209 try
210 {
211 // StLock<Mutex> _(gActivationMutex()); // force serialization of cursor creation
212 Keychain &kc = *mCurrent;
213 Mutex* mutex = kc->getKeychainMutex();
214 StLock<Mutex> _(*mutex);
215 (*mCurrent)->database()->activate();
216 mDbCursor = DbCursor((*mCurrent)->database(), *this);
217 }
218 catch(const CommonError &err)
219 {
220 ++mCurrent;
fa7225c8 221 mIsNewKeychain = true;
e3d460c9
A
222 }
223 }
224
225 Keychain &kc = *mCurrent;
b04fe171 226
e3d460c9
A
227 Mutex* mutex = kc->getKeychainMutex();
228 StLock<Mutex> _(*mutex);
229
230 bool gotRecord;
231 try
232 {
233 // Clear out existing attributes first!
234 // (the previous iteration may have left attributes from a different schema)
235 dbAttributes.clear();
236
237 gotRecord = mDbCursor->next(&dbAttributes, NULL, uniqueId);
238 mAllFailed = false;
239 }
240 catch(const CommonError &err)
241 {
242 // Catch the last error we get and move on to the next keychain
243 // This error will be returned when we reach the end of our keychain list
244 // iff all calls to KCCursorImpl::next failed
245 status = err.osStatus();
246 gotRecord = false;
247 dbAttributes.invalidate();
248 }
249 catch(...)
250 {
251 // Catch all other errors
252 status = errSecItemNotFound;
253 gotRecord = false;
254 }
b1ab9ed8 255
e3d460c9
A
256 // If we did not get a record from the current keychain or the current
257 // keychain did not exist skip to the next keychain in the list.
258 if (!gotRecord)
259 {
260 ++mCurrent;
261 mDbCursor = DbCursor();
262 // we'd like to call newKeychain(mCurrent) here, but to avoid deadlock
263 // we need to drop the current keychain's mutex first. Use this silly
264 // hack to void the stack Mutex object.
fa7225c8 265 mIsNewKeychain = true;
b1ab9ed8 266 continue;
e3d460c9
A
267 }
268
269 // If doing a search for all records, skip the db blob added by the CSPDL
270 if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_METADATA &&
271 mDbCursor->recordType() == CSSM_DL_DB_RECORD_ANY)
272 continue;
273
274 // Filter out group keys at this layer
275 if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY)
276 {
277 bool groupKey = false;
278 try
b1ab9ed8 279 {
e3d460c9
A
280 // fetch the key label attribute, if it exists
281 dbAttributes.add(KeySchema::Label);
282 Db db((*mCurrent)->database());
283 CSSM_RETURN getattr_result = CSSM_DL_DataGetFromUniqueRecordId(db->handle(), uniqueId, &dbAttributes, NULL);
284 if (getattr_result == CSSM_OK)
285 {
286 CssmDbAttributeData *label = dbAttributes.find(KeySchema::Label);
287 CssmData attrData;
288 if (label)
289 attrData = *label;
290 if (attrData.length() > 4 && !memcmp(attrData.data(), "ssgp", 4))
291 groupKey = true;
292 }
293 else
294 {
295 dbAttributes.invalidate();
296 }
b1ab9ed8 297 }
e3d460c9 298 catch (...) {}
b1ab9ed8 299
e3d460c9
A
300 if (groupKey)
301 continue;
302 }
303
304 // Create the Item
305 // Go though Keychain since item might already exist.
306 // This might throw a CSSMERR_DL_RECORD_NOT_FOUND or be otherwise invalid. If we're supposed to delete these items, delete them...
307 try {
308 tempItem = (*mCurrent)->item(dbAttributes.recordType(), uniqueId);
309 } catch(CssmError cssme) {
310 if (mDeleteInvalidRecords) {
311 // This is an invalid record for some reason; delete it and restart the loop
312 const char* errStr = cssmErrorString(cssme.error);
fa7225c8 313 secnotice("integrity", "deleting corrupt record because: %d %s", (int) cssme.error, errStr);
e3d460c9
A
314
315 deleteInvalidRecord(uniqueId);
316 // if deleteInvalidRecord doesn't throw, we want to restart the loop
317 continue;
318 } else {
319 throw;
320 }
321 }
b1ab9ed8 322 }
e3d460c9 323
e3d460c9 324 item = tempItem;
b1ab9ed8
A
325
326 break;
327 }
328
e3d460c9 329 // If we reach here, we've found an item
b1ab9ed8
A
330 return true;
331}
427c49bc 332
e3d460c9
A
333void KCCursorImpl::deleteInvalidRecord(DbUniqueRecord& uniqueId) {
334 // This might throw a RECORD_NOT_FOUND. Handle that, because we don't care if we're trying to delete the item.
335 try {
336 uniqueId->deleteRecord();
337 } catch(CssmError delcssme) {
338 if (delcssme.osStatus() == CSSMERR_DL_RECORD_NOT_FOUND) {
fa7225c8 339 secnotice("integrity", "couldn't delete nonexistent record (this is okay)");
e3d460c9
A
340 } else {
341 throw;
342 }
343 }
344}
345
427c49bc
A
346
347
348bool KCCursorImpl::mayDelete()
349{
350 if (mDbCursor.get() != NULL)
351 {
352 return mDbCursor->isIdle();
353 }
354 else
355 {
356 return true;
357 }
358}
e3d460c9
A
359
360void KCCursorImpl::setDeleteInvalidRecords(bool deleteRecord) {
361 mDeleteInvalidRecords = deleteRecord;
362}
363
364void KCCursorImpl::newKeychain(StorageManager::KeychainList::iterator kcIter) {
fa7225c8
A
365 if(!mIsNewKeychain) {
366 // We've already been called on this keychain, don't bother.
367 return;
368 }
369
e3d460c9
A
370 if(kcIter != mSearchList.end()) {
371 (*kcIter)->performKeychainUpgradeIfNeeded();
fa7225c8 372 (*kcIter)->tickle();
e3d460c9 373 }
fa7225c8
A
374
375 // Mark down that this function has been called
376 mIsNewKeychain = false;
e3d460c9 377}