1/* 2 * Copyright (c) 2000-2004 Apple Computer, 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 40using namespace KeychainCore; 41using namespace CssmClient; 42using namespace CSSMDateTimeUtils; 43 44using namespace KeySchema; 45 46// define a table of our attributes for easy lookup 47static const CSSM_DB_ATTRIBUTE_INFO* gKeyAttributeLookupTable[] = 48{ 49 &KeyClass, &PrintName, &Alias, &Permanent, &Private, &Modifiable, &Label, &ApplicationTag, &KeyCreator, 50 &KeyType, &KeySizeInBits, &EffectiveKeySize, &StartDate, &EndDate, &Sensitive, &AlwaysSensitive, &Extractable, 51 &NeverExtractable, &Encrypt, &Decrypt, &Derive, &Sign, &Verify, &SignRecover, &VerifyRecover, &Wrap, &Unwrap 52}; 53 54// 55// KCCursorImpl 56// 57KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList &searchList, SecItemClass itemClass, const SecKeychainAttributeList *attrList, CSSM_DB_CONJUNCTIVE dbConjunctive, CSSM_DB_OPERATOR dbOperator) : 58 mSearchList(searchList), 59 mCurrent(mSearchList.begin()), 60 mAllFailed(true), 61 mMutex(Mutex::recursive) 62{ 63 recordType(Schema::recordTypeFor(itemClass)); 64 65 if (!attrList) // No additional selectionPredicates: we are done 66 return; 67 68 conjunctive(dbConjunctive); 69 const SecKeychainAttribute *end=&attrList->attr[attrList->count]; 70 // Add all the attrs in attrs list to the cursor. 71 for (const SecKeychainAttribute *attr=attrList->attr; attr != end; ++attr) 72 { 73 const CSSM_DB_ATTRIBUTE_INFO *temp; 74 75 if (attr->tag <' ') // ok, is this a key schema? Handle differently, just because we can... 76 { 77 temp = gKeyAttributeLookupTable[attr->tag]; 78 } 79 else 80 { 81 temp = &Schema::attributeInfo(attr->tag); 82 } 83 const CssmDbAttributeInfo &info = *temp; 84 void *buf = attr->data; 85 UInt32 length = attr->length; 86 uint8 timeString[16]; 87 88 // XXX This code is duplicated in NewItemImpl::setAttribute() 89 // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE 90 // style attribute value. 91 if (info.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE) 92 { 93 if (length == sizeof(UInt32)) 94 { 95 MacSecondsToTimeString(*reinterpret_cast<const UInt32 *>(buf), 96 16, &timeString); 97 buf = &timeString; 98 length = 16; 99 } 100 else if (length == sizeof(SInt64)) 101 { 102 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 *>(buf), 103 16, &timeString); 104 buf = &timeString; 105 length = 16; 106 } 107 } 108 add(dbOperator ,info, CssmData(buf,length)); 109 } 110} 111 112KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList &searchList, const SecKeychainAttributeList *attrList) : 113 mSearchList(searchList), 114 mCurrent(mSearchList.begin()), 115 mAllFailed(true), 116 mMutex(Mutex::recursive) 117{ 118 if (!attrList) // No additional selectionPredicates: we are done 119 return; 120 121 conjunctive(CSSM_DB_AND); 122 bool foundClassAttribute=false; 123 const SecKeychainAttribute *end=&attrList->attr[attrList->count]; 124 // Add all the attrs in attrs list to the cursor. 125 for (const SecKeychainAttribute *attr=attrList->attr; attr != end; ++attr) 126 { 127 if (attr->tag!=kSecClassItemAttr) // a regular attribute 128 { 129 const CssmDbAttributeInfo &info = Schema::attributeInfo(attr->tag); 130 void *buf = attr->data; 131 UInt32 length = attr->length; 132 uint8 timeString[16]; 133 134 // XXX This code is duplicated in NewItemImpl::setAttribute() 135 // Convert a 4 or 8 byte TIME_DATE to a CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE 136 // style attribute value. 137 if (info.format() == CSSM_DB_ATTRIBUTE_FORMAT_TIME_DATE) 138 { 139 if (length == sizeof(UInt32)) 140 { 141 MacSecondsToTimeString(*reinterpret_cast<const UInt32 *>(buf), 142 16, &timeString); 143 buf = &timeString; 144 length = 16; 145 } 146 else if (length == sizeof(SInt64)) 147 { 148 MacLongDateTimeToTimeString(*reinterpret_cast<const SInt64 *>(buf), 149 16, &timeString); 150 buf = &timeString; 151 length = 16; 152 } 153 } 154 add(CSSM_DB_EQUAL,info, CssmData(buf,length)); 155 156 continue; 157 } 158 159 // the class attribute 160 if (foundClassAttribute || attr->length != sizeof(SecItemClass)) 161 MacOSError::throwMe(errSecParam); // We have 2 different 'clas' attributes 162 163 recordType(Schema::recordTypeFor(*reinterpret_cast<SecItemClass *>(attr->data))); 164 foundClassAttribute=true; 165 } 166} 167 168KCCursorImpl::~KCCursorImpl() throw() 169{ 170} 171 172//static ModuleNexus<Mutex> gActivationMutex; 173 174bool 175KCCursorImpl::next(Item &item) 176{ 177 StLock<Mutex>_(mMutex); 178 DbAttributes dbAttributes; 179 DbUniqueRecord uniqueId; 180 OSStatus status = 0; 181 182 for (;;) 183 { 184 while (!mDbCursor) 185 { 186 if (mCurrent == mSearchList.end()) 187 { 188 // If we got always failed when calling mDbCursor->next return the error from 189 // the last call to mDbCursor->next now 190 if (mAllFailed && status) 191 CssmError::throwMe(status); 192 193 // No more keychains to search so we are done. 194 return false; 195 } 196 197 try 198 { 199 // StLock<Mutex> _(gActivationMutex()); // force serialization of cursor creation 200 Keychain &kc = *mCurrent; 201 Mutex* mutex = kc->getKeychainMutex(); 202 StLock<Mutex> _(*mutex); 203 (*mCurrent)->database()->activate(); 204 mDbCursor = DbCursor((*mCurrent)->database(), *this); 205 } 206 catch(const CommonError &err) 207 { 208 ++mCurrent; 209 } 210 } 211 212 Keychain &kc = *mCurrent; 213 Mutex* mutex = kc->getKeychainMutex(); 214 StLock<Mutex> _(*mutex); 215 216 bool gotRecord; 217 try 218 { 219 // Clear out existing attributes first! 220 // (the previous iteration may have left attributes from a different schema) 221 dbAttributes.clear(); 222 223 gotRecord = mDbCursor->next(&dbAttributes, NULL, uniqueId); 224 mAllFailed = false; 225 } 226 catch(const CommonError &err) 227 { 228 // Catch the last error we get and move on to the next keychain 229 // This error will be returned when we reach the end of our keychain list 230 // iff all calls to KCCursorImpl::next failed 231 status = err.osStatus(); 232 gotRecord = false; 233 dbAttributes.invalidate(); 234 } 235 catch(...) 236 { 237 // Catch all other errors 238 status = errSecItemNotFound; 239 gotRecord = false; 240 } 241 242 // If we did not get a record from the current keychain or the current 243 // keychain did not exist skip to the next keychain in the list. 244 if (!gotRecord) 245 { 246 ++mCurrent; 247 mDbCursor = DbCursor(); 248 continue; 249 } 250 251 // If doing a search for all records, skip the db blob added by the CSPDL 252 if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_METADATA && 253 mDbCursor->recordType() == CSSM_DL_DB_RECORD_ANY) 254 continue; 255 256 // Filter out group keys at this layer 257 if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY) 258 { 259 bool groupKey = false; 260 try 261 { 262 // fetch the key label attribute, if it exists 263 dbAttributes.add(KeySchema::Label); 264 Db db((*mCurrent)->database()); 265 CSSM_RETURN getattr_result = CSSM_DL_DataGetFromUniqueRecordId(db->handle(), uniqueId, &dbAttributes, NULL); 266 if (getattr_result == CSSM_OK) 267 { 268 CssmDbAttributeData *label = dbAttributes.find(KeySchema::Label); 269 CssmData attrData; 270 if (label) 271 attrData = *label; 272 if (attrData.length() > 4 && !memcmp(attrData.data(), "ssgp", 4)) 273 groupKey = true; 274 } 275 else 276 { 277 dbAttributes.invalidate(); 278 } 279 } 280 catch (...) {} 281 282 if (groupKey) 283 continue; 284 } 285 286 break; 287 } 288 289 // Go though Keychain since item might already exist. 290 Keychain &kc = *mCurrent; 291 StLock<Mutex> _mutexLocker(*kc->getKeychainMutex()); 292 item = (*mCurrent)->item(dbAttributes.recordType(), uniqueId); 293 return true; 294} 295 296 297 298bool KCCursorImpl::mayDelete() 299{ 300 if (mDbCursor.get() != NULL) 301 { 302 return mDbCursor->isIdle(); 303 } 304 else 305 { 306 return true; 307 } 308} 309