1/* 2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. 3 * 4 * The contents of this file constitute Original Code as defined in and are 5 * subject to the Apple Public Source License Version 1.2 (the 'License'). 6 * You may not use this file except in compliance with the License. Please obtain 7 * a copy of the License at http://www.apple.com/publicsource and read it before 8 * using this file. 9 * 10 * This Original Code and all software distributed under the License are 11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS 12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT 13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the 15 * specific language governing rights and limitations under the License. 16 */ 17 18 19// 20// SSDatabase.cpp - Security Server database object 21// 22#include "SSDatabase.h" 23 24#include <security_cdsa_utilities/KeySchema.h> 25 26using namespace CssmClient; 27using namespace SecurityServer; 28 29const char *const SSDatabaseImpl::DBBlobRelationName = "DBBlob"; 30 31 32SSDatabaseImpl::SSDatabaseImpl(ClientSession &inClientSession, const CssmClient::DL &dl, 33 const char *inDbName, const CSSM_NET_ADDRESS *inDbLocation) 34 : Db::Impl(dl, inDbName, inDbLocation), mClientSession(inClientSession), mSSDbHandle(noDb) 35{ 36} 37 38SSDatabaseImpl::~SSDatabaseImpl() 39try 40{ 41 if (mSSDbHandle != noDb) 42 mClientSession.releaseDb(mSSDbHandle); 43} 44catch (...) 45{ 46} 47 48SSUniqueRecord 49SSDatabaseImpl::insert(CSSM_DB_RECORDTYPE recordType, 50 const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes, 51 const CSSM_DATA *data, bool) 52{ 53 SSUniqueRecord uniqueId(SSDatabase(this)); 54 check(CSSM_DL_DataInsert(handle(), recordType, 55 attributes, 56 data, uniqueId)); 57 // Activate uniqueId so CSSM_DL_FreeUniqueRecord() gets called when it goes out of scope. 58 uniqueId->activate(); 59 return uniqueId; 60} 61 62void 63SSDatabaseImpl::authenticate(CSSM_DB_ACCESS_TYPE inAccessRequest, 64 const CSSM_ACCESS_CREDENTIALS *inAccessCredentials) 65{ 66 mClientSession.authenticateDb(dbHandle(), inAccessRequest, 67 AccessCredentials::overlay(inAccessCredentials)); 68} 69 70void 71SSDatabaseImpl::lock() 72{ 73 mClientSession.lock(dbHandle()); 74 75} 76 77void 78SSDatabaseImpl::unlock() 79{ 80 mClientSession.unlock(dbHandle()); 81} 82 83void 84SSDatabaseImpl::unlock(const CSSM_DATA &password) 85{ 86 mClientSession.unlock(dbHandle(), CssmData::overlay(password)); 87} 88 89void 90SSDatabaseImpl::stash() 91{ 92 mClientSession.stashDb(dbHandle()); 93} 94 95void 96SSDatabaseImpl::stashCheck() 97{ 98 mClientSession.stashDbCheck(dbHandle()); 99} 100 101void 102SSDatabaseImpl::getSettings(uint32 &outIdleTimeout, bool &outLockOnSleep) 103{ 104 DBParameters parameters; 105 mClientSession.getDbParameters(dbHandle(), parameters); 106 outIdleTimeout = parameters.idleTimeout; 107 outLockOnSleep = parameters.lockOnSleep; 108} 109 110void 111SSDatabaseImpl::setSettings(uint32 inIdleTimeout, bool inLockOnSleep) 112{ 113 DBParameters parameters; 114 parameters.idleTimeout = inIdleTimeout; 115 parameters.lockOnSleep = inLockOnSleep; 116 mClientSession.setDbParameters(dbHandle(), parameters); 117 118 // Reencode the db blob. 119 CssmDataContainer dbb(allocator()); 120 mClientSession.encodeDb(mSSDbHandle, dbb, allocator()); 121 getDbBlobId()->modify(DBBlobRelationID, NULL, &dbb, CSSM_DB_MODIFY_ATTRIBUTE_NONE); 122} 123 124bool 125SSDatabaseImpl::isLocked() 126{ 127 return mClientSession.isLocked(dbHandle()); 128} 129 130void 131SSDatabaseImpl::changePassphrase(const CSSM_ACCESS_CREDENTIALS *cred) 132{ 133 mClientSession.changePassphrase(dbHandle(), AccessCredentials::overlay(cred)); 134 135 // Reencode the db blob. 136 CssmDataContainer dbb(allocator()); 137 mClientSession.encodeDb(mSSDbHandle, dbb, allocator()); 138 getDbBlobId()->modify(DBBlobRelationID, NULL, &dbb, CSSM_DB_MODIFY_ATTRIBUTE_NONE); 139} 140 141DbHandle 142SSDatabaseImpl::dbHandle() 143{ 144 activate(); 145 if (mForked()) { 146 // re-establish the dbHandle with the SecurityServer 147 CssmDataContainer dbb(allocator()); 148 getDbBlobId(&dbb); 149 mSSDbHandle = mClientSession.decodeDb(mIdentifier, 150 AccessCredentials::overlay(accessCredentials()), dbb); 151 } 152 return mSSDbHandle; 153} 154 155void 156SSDatabaseImpl::commonCreate(const DLDbIdentifier &dlDbIdentifier, bool &autoCommit) 157{ 158 mIdentifier = dlDbIdentifier; 159 // Set to false if autocommit should remain off after the create. 160 autoCommit = true; 161 162 // OpenParameters to use 163 CSSM_APPLEDL_OPEN_PARAMETERS newOpenParameters = 164 { 165 sizeof(CSSM_APPLEDL_OPEN_PARAMETERS), 166 CSSM_APPLEDL_OPEN_PARAMETERS_VERSION, 167 CSSM_FALSE, // do not auto-commit 168 0 // mask - do not use following fields 169 }; 170 171 // Get the original openParameters and apply them to the ones we 172 // are passing in. 173 const CSSM_APPLEDL_OPEN_PARAMETERS *inOpenParameters = 174 reinterpret_cast<const CSSM_APPLEDL_OPEN_PARAMETERS *>(openParameters()); 175 if (inOpenParameters) 176 { 177 switch (inOpenParameters->version) 178 { 179 case 1: 180 if (inOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS)) 181 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS); 182 183 newOpenParameters.mask = inOpenParameters->mask; 184 newOpenParameters.mode = inOpenParameters->mode; 185 /*DROPTHROUGH*/ 186 case 0: 187 //if (inOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0)) 188 // CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS); 189 190 // This will determine whether we leave autocommit off or not. 191 autoCommit = inOpenParameters->autoCommit == CSSM_FALSE ? false : true; 192 break; 193 194 default: 195 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS); 196 } 197 } 198 199 // Use the new openParameters 200 openParameters(&newOpenParameters); 201 try 202 { 203 DbImpl::create(); 204 // Restore the original openparameters again. 205 openParameters(inOpenParameters); 206 } 207 catch (...) 208 { 209 // Make sure restore the original openparameters again even if 210 // create throws. 211 openParameters(inOpenParameters); 212 throw; 213 } 214 215 // @@@ The CSSM_DB_SCHEMA_ATTRIBUTE_INFO and CSSM_DB_SCHEMA_INDEX_INFO 216 // arguments should be optional. 217 createRelation(DBBlobRelationID, DBBlobRelationName, 218 0, (CSSM_DB_SCHEMA_ATTRIBUTE_INFO *)42, 219 0, (CSSM_DB_SCHEMA_INDEX_INFO *)42); 220 221 // @@@ Only iff not already in mDbInfo 222 createRelation(CSSM_DL_DB_RECORD_PUBLIC_KEY, "CSSM_DL_DB_RECORD_PUBLIC_KEY", 223 KeySchema::KeySchemaAttributeCount, KeySchema::KeySchemaAttributeList, 224 KeySchema::KeySchemaIndexCount, KeySchema::KeySchemaIndexList); 225 226 // @@@ Only iff not already in mDbInfo 227 createRelation(CSSM_DL_DB_RECORD_PRIVATE_KEY, "CSSM_DL_DB_RECORD_PRIVATE_KEY", 228 KeySchema::KeySchemaAttributeCount, KeySchema::KeySchemaAttributeList, 229 KeySchema::KeySchemaIndexCount, KeySchema::KeySchemaIndexList); 230 231 // @@@ Only iff not already in mDbInfo 232 createRelation(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, "CSSM_DL_DB_RECORD_SYMMETRIC_KEY", 233 KeySchema::KeySchemaAttributeCount, KeySchema::KeySchemaAttributeList, 234 KeySchema::KeySchemaIndexCount, KeySchema::KeySchemaIndexList); 235} 236 237void 238SSDatabaseImpl::create(const DLDbIdentifier &dlDbIdentifier) 239{ 240 try 241 { 242 bool autoCommit; 243 commonCreate(dlDbIdentifier, autoCommit); 244 245 DBParameters dbParameters; 246 memset(&dbParameters, 0, sizeof(DBParameters)); 247 dbParameters.idleTimeout = kDefaultIdleTimeout; 248 dbParameters.lockOnSleep = kDefaultLockOnSleep; 249 250 const AccessCredentials *cred = NULL; 251 const AclEntryInput *owner = NULL; 252 if (resourceControlContext()) 253 { 254 cred = AccessCredentials::overlay(resourceControlContext()->AccessCred); 255 owner = &AclEntryInput::overlay(resourceControlContext()->InitialAclEntry); 256 } 257 mSSDbHandle = mClientSession.createDb(dlDbIdentifier, cred, owner, dbParameters); 258 CssmDataContainer dbb(allocator()); 259 mClientSession.encodeDb(mSSDbHandle, dbb, allocator()); 260 Db::Impl::insert(DBBlobRelationID, NULL, &dbb); 261 if (autoCommit) 262 { 263 passThrough(CSSM_APPLEFILEDL_COMMIT, NULL); 264 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 265 reinterpret_cast<const void *>(1)); 266 } 267 } 268 catch(CssmError e) 269 { 270 if (e.error != CSSMERR_DL_DATASTORE_ALREADY_EXISTS) 271 { 272 DbImpl::deleteDb(); 273 } 274 throw; 275 } 276 catch(...) 277 { 278 DbImpl::deleteDb(); 279 throw; 280 } 281} 282 283void 284SSDatabaseImpl::createWithBlob(const DLDbIdentifier &dlDbIdentifier, const CSSM_DATA &blob) 285{ 286 try 287 { 288 bool autoCommit; 289 commonCreate(dlDbIdentifier, autoCommit); 290 Db::Impl::insert(DBBlobRelationID, NULL, &blob); 291 if (autoCommit) 292 { 293 passThrough(CSSM_APPLEFILEDL_COMMIT, NULL); 294 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 295 reinterpret_cast<const void *>(1)); 296 } 297 } 298 catch(...) 299 { 300 DbImpl::deleteDb(); 301 throw; 302 } 303} 304 305void 306SSDatabaseImpl::open(const DLDbIdentifier &dlDbIdentifier) 307{ 308 mIdentifier = dlDbIdentifier; 309 Db::Impl::open(); 310 311 CssmDataContainer dbb(allocator()); 312 getDbBlobId(&dbb); 313 314 mSSDbHandle = mClientSession.decodeDb(dlDbIdentifier, AccessCredentials::overlay(accessCredentials()), dbb); 315} 316 317void 318SSDatabaseImpl::recode(const CssmData &data, const CssmData &extraData) 319{ 320 // Start a transaction (Implies activate()). 321 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 0); 322 323 try 324 { 325 CssmDataContainer dbb(allocator()); 326 // Make sure mSSDbHandle is valid. 327 CssmClient::DbUniqueRecord dbBlobId = getDbBlobId(&dbb); 328 if (mForked()) { 329 // re-establish the dbHandle with the SecurityServer 330 mSSDbHandle = mClientSession.decodeDb(mIdentifier, 331 AccessCredentials::overlay(accessCredentials()), dbb); 332 } 333 dbb.clear(); 334 335 DbHandle successfulHdl = mClientSession.authenticateDbsForSync(data, extraData); 336 337 // Create a newDbHandle using the master secrets from the dbBlob we are 338 // recoding to. 339 SecurityServer::DbHandle clonedDbHandle = 340 mClientSession.recodeDbForSync(successfulHdl, mSSDbHandle); 341 342 // @@@ If the dbb changed since we fetched it we should abort or 343 // retry the operation here. 344 345 // Recode all keys 346 DbCursor cursor(SSDatabase(this)); 347 cursor->recordType(CSSM_DL_DB_RECORD_ALL_KEYS); 348 CssmDataContainer keyBlob(allocator()); 349 CssmClient::DbUniqueRecord keyBlobId; 350 DbAttributes attributes; 351 while (cursor->next(&attributes, &keyBlob, keyBlobId)) 352 { 353 // Decode the old key 354 CssmKey::Header header; 355 KeyHandle keyHandle = 356 mClientSession.decodeKey(mSSDbHandle, keyBlob, header); 357 // Recode the key 358 CssmDataContainer newKeyBlob(mClientSession.returnAllocator); 359 mClientSession.recodeKey(mSSDbHandle, keyHandle, clonedDbHandle, 360 newKeyBlob); 361 mClientSession.releaseKey(keyHandle); 362 // Write the recoded key blob to the database 363 keyBlobId->modify(attributes.recordType(), NULL, &newKeyBlob, 364 CSSM_DB_MODIFY_ATTRIBUTE_NONE); 365 } 366 367 // Commit the new blob to securityd, reencode the db blob, release the 368 // cloned db handle and commit the new blob to the db. 369 mClientSession.commitDbForSync(mSSDbHandle, clonedDbHandle, 370 dbb, allocator()); 371 dbBlobId->modify(DBBlobRelationID, NULL, &dbb, 372 CSSM_DB_MODIFY_ATTRIBUTE_NONE); 373 374 // Commit the transaction to the db 375 passThrough(CSSM_APPLEFILEDL_COMMIT, NULL); 376 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 377 reinterpret_cast<const void *>(1)); 378 } 379 catch (...) 380 { 381 // Something went wrong rollback the transaction 382 passThrough(CSSM_APPLEFILEDL_ROLLBACK, NULL); 383 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 384 reinterpret_cast<const void *>(1)); 385 throw; 386 } 387} 388 389void SSDatabaseImpl::getRecordIdentifier(CSSM_DB_UNIQUE_RECORD_PTR uniqueRecord, CSSM_DATA &recordID) 390{ 391 // the unique ID is composed of three uint32s (plus one filler word). Pull 392 // them out and byte swap them 393 recordID.Length = sizeof (uint32) * kNumIDWords; 394 recordID.Data = (uint8*) allocator().malloc(recordID.Length); 395 396 // copy the data 397 uint32* dest = (uint32*) recordID.Data; 398 uint32* src = (uint32*) uniqueRecord->RecordIdentifier.Data; 399 400 dest[0] = htonl (src[0]); 401 dest[1] = htonl (src[1]); 402 dest[2] = htonl (src[2]); 403 dest[3] = 0; 404} 405 406void SSDatabaseImpl::copyBlob(CSSM_DATA &data) 407{ 408 // get the blob from the database 409 CssmDataContainer dbb(allocator()); 410 getDbBlobId(&dbb); 411 412 // copy the data back 413 data.Data = dbb.Data; 414 data.Length = dbb.Length; 415 416 // zap the return structure so that we don't get zapped when dbb goes out of scope... 417 dbb.Data = NULL; 418 dbb.Length = 0; 419} 420 421DbUniqueRecordImpl * 422SSDatabaseImpl::newDbUniqueRecord() 423{ 424 return new SSUniqueRecordImpl(SSDatabase(this)); 425} 426 427CssmClient::DbUniqueRecord 428SSDatabaseImpl::getDbBlobId(CssmDataContainer *dbb) 429{ 430 CssmClient::DbUniqueRecord dbBlobId; 431 432 DbCursor cursor(SSDatabase(this)); 433 cursor->recordType(DBBlobRelationID); 434 if (!cursor->next(NULL, dbb, dbBlobId)) 435 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT); 436 437 return dbBlobId; 438} 439 440 441 442SSUniqueRecordImpl::SSUniqueRecordImpl(const SSDatabase &db) 443: DbUniqueRecord::Impl(db) 444{ 445} 446 447SSUniqueRecordImpl::~SSUniqueRecordImpl() 448{ 449} 450 451SSDatabase 452SSUniqueRecordImpl::database() const 453{ 454 return parent<SSDatabase>(); 455} 456