/ OSX / libsecurity_keychain / lib / KCCursor.cpp
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 <Security/SecBase.h>
 39  #include <Security/SecBasePriv.h>
 40  #include <utilities/array_size.h>
 41  
 42  using namespace KeychainCore;
 43  using namespace CssmClient;
 44  using namespace CSSMDateTimeUtils;
 45  
 46  using namespace KeySchema;
 47  
 48  // define a table of our attributes for easy lookup
 49  static 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  //
 59  KCCursorImpl::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),
 63      mDeleteInvalidRecords(false),
 64      mIsNewKeychain(true),
 65  	mMutex(Mutex::recursive)
 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  		// ok, is this a key schema?  Handle differently, just because we can...
 80  		if (attr->tag <'    ' && attr->tag < array_size(gKeyAttributeLookupTable))
 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  
117  KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList &searchList, const SecKeychainAttributeList *attrList) :
118  	mSearchList(searchList),
119  	mCurrent(mSearchList.begin()),
120  	mAllFailed(true),
121      mDeleteInvalidRecords(false),
122      mIsNewKeychain(true),
123  	mMutex(Mutex::recursive)
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() _NOEXCEPT
176  {
177  }
178  
179  //static ModuleNexus<Mutex> gActivationMutex;
180  
181  bool
182  KCCursorImpl::next(Item &item)
183  {
184  	StLock<Mutex>_(mMutex);
185  	DbAttributes dbAttributes;
186  	DbUniqueRecord uniqueId;
187  	OSStatus status = 0;
188  
189  	for (;;)
190  	{
191          Item tempItem = NULL;
192          {
193              while (!mDbCursor)
194              {
195                  // Do the newKeychain dance before we check our done status
196                  newKeychain(mCurrent);
197  
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);
204  
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;
221                      mIsNewKeychain = true;
222                  }
223              }
224  
225              Keychain &kc = *mCurrent;
226  
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              }
255  
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.
265                  mIsNewKeychain = true;
266                  continue;
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
279                  {
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                      }
297                  }
298                  catch (...) {}
299  
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);
313                      secnotice("integrity", "deleting corrupt record because: %d %s", (int) cssme.error, errStr);
314  
315                      deleteInvalidRecord(uniqueId);
316                      // if deleteInvalidRecord doesn't throw, we want to restart the loop
317                      continue;
318                  } else {
319                      throw;
320                  }
321              }
322          }
323  
324  		item = tempItem;
325  
326  		break;
327  	}
328  
329  	// If we reach here, we've found an item
330  	return true;
331  }
332  
333  void 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) {
339              secnotice("integrity", "couldn't delete nonexistent record (this is okay)");
340          } else {
341              throw;
342          }
343      }
344  }
345  
346  
347  
348  bool KCCursorImpl::mayDelete()
349  {
350      if (mDbCursor.get() != NULL)
351      {
352          return mDbCursor->isIdle();
353      }
354      else
355      {
356          return true;
357      }
358  }
359  
360  void KCCursorImpl::setDeleteInvalidRecords(bool deleteRecord) {
361      mDeleteInvalidRecords = deleteRecord;
362  }
363  
364  void KCCursorImpl::newKeychain(StorageManager::KeychainList::iterator kcIter) {
365      if(!mIsNewKeychain) {
366          // We've already been called on this keychain, don't bother.
367          return;
368      }
369  
370      if(kcIter != mSearchList.end()) {
371          (*kcIter)->performKeychainUpgradeIfNeeded();
372          (*kcIter)->tickle();
373      }
374  
375      // Mark down that this function has been called
376      mIsNewKeychain = false;
377  }