2  * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   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 
  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. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  32 #include <security_cdsa_utilities/Schema.h> 
  33 #include <security_cdsa_utilities/KeySchema.h> 
  34 #include "cssmdatetime.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> 
  42 using namespace KeychainCore
; 
  43 using namespace CssmClient
; 
  44 using namespace CSSMDateTimeUtils
; 
  46 using namespace KeySchema
; 
  48 // define a table of our attributes for easy lookup 
  49 static const CSSM_DB_ATTRIBUTE_INFO
* gKeyAttributeLookupTable
[] = 
  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
 
  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()), 
  63     mDeleteInvalidRecords(false), 
  65         mMutex(Mutex::recursive
) 
  67     recordType(Schema::recordTypeFor(itemClass
)); 
  69         if (!attrList
) // No additional selectionPredicates: we are done 
  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
) 
  77                 const CSSM_DB_ATTRIBUTE_INFO 
*temp
; 
  79                 // ok, is this a key schema?  Handle differently, just because we can... 
  80                 if (attr
->tag 
<'    ' && attr
->tag 
< array_size(gKeyAttributeLookupTable
)) 
  82                         temp 
= gKeyAttributeLookupTable
[attr
->tag
]; 
  86                         temp 
= &Schema::attributeInfo(attr
->tag
); 
  88         const CssmDbAttributeInfo 
&info 
= *temp
; 
  89         void *buf 
= attr
->data
; 
  90         UInt32 length 
= attr
->length
; 
  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
) 
  98             if (length 
== sizeof(UInt32
)) 
 100                 MacSecondsToTimeString(*reinterpret_cast<const UInt32 
*>(buf
), 
 105             else if (length 
== sizeof(SInt64
)) 
 107                 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 
*>(buf
), 
 113         add(dbOperator 
,info
, CssmData(buf
,length
)); 
 117 KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList 
&searchList
, const SecKeychainAttributeList 
*attrList
) : 
 118         mSearchList(searchList
), 
 119         mCurrent(mSearchList
.begin()), 
 121     mDeleteInvalidRecords(false), 
 122     mIsNewKeychain(true), 
 123         mMutex(Mutex::recursive
) 
 125         if (!attrList
) // No additional selectionPredicates: we are done 
 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
) 
 134                 if (attr
->tag
!=kSecClassItemAttr
)       // a regular attribute 
 136             const CssmDbAttributeInfo 
&info 
= Schema::attributeInfo(attr
->tag
); 
 137             void *buf 
= attr
->data
; 
 138             UInt32 length 
= attr
->length
; 
 139             uint8 timeString
[16]; 
 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
) 
 146                 if (length 
== sizeof(UInt32
)) 
 148                     MacSecondsToTimeString(*reinterpret_cast<const UInt32 
*>(buf
), 
 153                 else if (length 
== sizeof(SInt64
)) 
 155                     MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 
*>(buf
), 
 161                         add(CSSM_DB_EQUAL
,info
, CssmData(buf
,length
)); 
 166                 // the class attribute 
 167                 if (foundClassAttribute 
|| attr
->length 
!= sizeof(SecItemClass
)) 
 168                         MacOSError::throwMe(errSecParam
); // We have 2 different 'clas' attributes 
 170                 recordType(Schema::recordTypeFor(*reinterpret_cast<SecItemClass 
*>(attr
->data
))); 
 171                 foundClassAttribute
=true; 
 175 KCCursorImpl::~KCCursorImpl() throw() 
 179 //static ModuleNexus<Mutex> gActivationMutex; 
 182 KCCursorImpl::next(Item 
&item
) 
 184         StLock
<Mutex
>_(mMutex
); 
 185         DbAttributes dbAttributes
; 
 186         DbUniqueRecord uniqueId
; 
 191         Item tempItem 
= NULL
; 
 195                 // Do the newKeychain dance before we check our done status 
 196                 newKeychain(mCurrent
); 
 198                 if (mCurrent 
== mSearchList
.end()) 
 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
); 
 205                     // No more keychains to search so we are done. 
 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); 
 218                 catch(const CommonError 
&err
) 
 221                     mIsNewKeychain 
= true; 
 225             Keychain 
&kc 
= *mCurrent
; 
 227             Mutex
* mutex 
= kc
->getKeychainMutex(); 
 228             StLock
<Mutex
> _(*mutex
); 
 233                 // Clear out existing attributes first! 
 234                 // (the previous iteration may have left attributes from a different schema) 
 235                 dbAttributes
.clear(); 
 237                 gotRecord 
= mDbCursor
->next(&dbAttributes
, NULL
, uniqueId
); 
 240             catch(const CommonError 
&err
) 
 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(); 
 247                 dbAttributes
.invalidate(); 
 251                 // Catch all other errors 
 252                 status 
= errSecItemNotFound
; 
 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. 
 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; 
 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
) 
 274             // Filter out group keys at this layer 
 275             if (dbAttributes
.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY
) 
 277                 bool groupKey 
= false; 
 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
) 
 286                         CssmDbAttributeData 
*label 
= dbAttributes
.find(KeySchema::Label
); 
 290                         if (attrData
.length() > 4 && !memcmp(attrData
.data(), "ssgp", 4)) 
 295                         dbAttributes
.invalidate(); 
 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... 
 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
); 
 315                     deleteInvalidRecord(uniqueId
); 
 316                     // if deleteInvalidRecord doesn't throw, we want to restart the loop 
 329         // If we reach here, we've found an item 
 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. 
 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)"); 
 348 bool KCCursorImpl::mayDelete() 
 350     if (mDbCursor
.get() != NULL
) 
 352         return mDbCursor
->isIdle(); 
 360 void KCCursorImpl::setDeleteInvalidRecords(bool deleteRecord
) { 
 361     mDeleteInvalidRecords 
= deleteRecord
; 
 364 void KCCursorImpl::newKeychain(StorageManager::KeychainList::iterator kcIter
) { 
 365     if(!mIsNewKeychain
) { 
 366         // We've already been called on this keychain, don't bother. 
 370     if(kcIter 
!= mSearchList
.end()) { 
 371         (*kcIter
)->performKeychainUpgradeIfNeeded(); 
 375     // Mark down that this function has been called 
 376     mIsNewKeychain 
= false;