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 }