2 * Copyright (c) 2000-2001,2011-2014 Apple Inc. All Rights Reserved.
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
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.
20 // SSDatabase.cpp - Security Server database object
22 #include "SSDatabase.h"
24 #include <security_cdsa_utilities/KeySchema.h>
25 #include <security_utilities/CSPDLTransaction.h>
26 #include <Security/SecBasePriv.h>
28 using namespace CssmClient
;
29 using namespace SecurityServer
;
31 const char *const SSDatabaseImpl::DBBlobRelationName
= "DBBlob";
34 SSDatabaseImpl::SSDatabaseImpl(ClientSession
&inClientSession
, const CssmClient::DL
&dl
,
35 const char *inDbName
, const CSSM_NET_ADDRESS
*inDbLocation
)
36 : Db::Impl(dl
, inDbName
, inDbLocation
), mClientSession(inClientSession
), mSSDbHandle(noDb
)
41 SSDatabaseImpl::~SSDatabaseImpl()
44 if (mSSDbHandle
!= noDb
)
45 mClientSession
.releaseDb(mSSDbHandle
);
49 return; // Prevent re-throw of exception [function-try-block]
53 SSDatabaseImpl::ssInsert(CSSM_DB_RECORDTYPE recordType
,
54 const CSSM_DB_RECORD_ATTRIBUTE_DATA
*attributes
,
55 const CSSM_DATA
*data
)
57 SSUniqueRecord
uniqueId(SSDatabase(this));
58 check(CSSM_DL_DataInsert(handle(), recordType
,
61 // Activate uniqueId so CSSM_DL_FreeUniqueRecord() gets called when it goes out of scope.
67 SSDatabaseImpl::authenticate(CSSM_DB_ACCESS_TYPE inAccessRequest
,
68 const CSSM_ACCESS_CREDENTIALS
*inAccessCredentials
)
70 mClientSession
.authenticateDb(dbHandle(), inAccessRequest
,
71 AccessCredentials::overlay(inAccessCredentials
));
75 SSDatabaseImpl::lock()
77 mClientSession
.lock(dbHandle());
82 SSDatabaseImpl::unlock()
84 mClientSession
.unlock(dbHandle());
88 SSDatabaseImpl::unlock(const CSSM_DATA
&password
)
90 mClientSession
.unlock(dbHandle(), CssmData::overlay(password
));
94 SSDatabaseImpl::stash()
96 mClientSession
.stashDb(dbHandle());
100 SSDatabaseImpl::stashCheck()
102 mClientSession
.stashDbCheck(dbHandle());
106 SSDatabaseImpl::getSettings(uint32
&outIdleTimeout
, bool &outLockOnSleep
)
108 DBParameters parameters
;
109 mClientSession
.getDbParameters(dbHandle(), parameters
);
110 outIdleTimeout
= parameters
.idleTimeout
;
111 outLockOnSleep
= parameters
.lockOnSleep
;
115 SSDatabaseImpl::setSettings(uint32 inIdleTimeout
, bool inLockOnSleep
)
117 DBParameters parameters
;
118 parameters
.idleTimeout
= inIdleTimeout
;
119 parameters
.lockOnSleep
= inLockOnSleep
;
120 mClientSession
.setDbParameters(dbHandle(), parameters
);
122 // Reencode the db blob.
123 CssmDataContainer
dbb(allocator());
124 mClientSession
.encodeDb(mSSDbHandle
, dbb
, allocator());
125 getDbBlobId()->modify(DBBlobRelationID
, NULL
, &dbb
, CSSM_DB_MODIFY_ATTRIBUTE_NONE
);
129 SSDatabaseImpl::isLocked()
131 return mClientSession
.isLocked(dbHandle());
135 SSDatabaseImpl::changePassphrase(const CSSM_ACCESS_CREDENTIALS
*cred
)
137 mClientSession
.changePassphrase(dbHandle(), AccessCredentials::overlay(cred
));
139 // Reencode the db blob.
140 CssmDataContainer
dbb(allocator());
141 mClientSession
.encodeDb(mSSDbHandle
, dbb
, allocator());
142 getDbBlobId()->modify(DBBlobRelationID
, NULL
, &dbb
, CSSM_DB_MODIFY_ATTRIBUTE_NONE
);
146 SSDatabaseImpl::dbHandle()
150 // re-establish the dbHandle with the SecurityServer
151 CssmDataContainer
dbb(allocator());
153 mSSDbHandle
= mClientSession
.decodeDb(mIdentifier
,
154 AccessCredentials::overlay(accessCredentials()), dbb
);
160 SSDatabaseImpl::commonCreate(const DLDbIdentifier
&dlDbIdentifier
, bool &autoCommit
)
162 mIdentifier
= dlDbIdentifier
;
163 // Set to false if autocommit should remain off after the create.
166 // OpenParameters to use
167 CSSM_APPLEDL_OPEN_PARAMETERS newOpenParameters
=
169 sizeof(CSSM_APPLEDL_OPEN_PARAMETERS
),
170 CSSM_APPLEDL_OPEN_PARAMETERS_VERSION
,
171 CSSM_FALSE
, // do not auto-commit
172 0 // mask - do not use following fields
175 // Get the original openParameters and apply them to the ones we
177 const CSSM_APPLEDL_OPEN_PARAMETERS
*inOpenParameters
=
178 reinterpret_cast<const CSSM_APPLEDL_OPEN_PARAMETERS
*>(openParameters());
179 if (inOpenParameters
)
181 switch (inOpenParameters
->version
)
184 if (inOpenParameters
->length
< sizeof(CSSM_APPLEDL_OPEN_PARAMETERS
))
185 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS
);
187 newOpenParameters
.mask
= inOpenParameters
->mask
;
188 newOpenParameters
.mode
= inOpenParameters
->mode
;
191 //if (inOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0))
192 // CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
194 // This will determine whether we leave autocommit off or not.
195 autoCommit
= inOpenParameters
->autoCommit
== CSSM_FALSE
? false : true;
199 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS
);
203 // Use the new openParameters
204 openParameters(&newOpenParameters
);
208 // Restore the original openparameters again.
209 openParameters(inOpenParameters
);
213 // Make sure restore the original openparameters again even if
215 openParameters(inOpenParameters
);
219 // @@@ The CSSM_DB_SCHEMA_ATTRIBUTE_INFO and CSSM_DB_SCHEMA_INDEX_INFO
220 // arguments should be optional.
221 createRelation(DBBlobRelationID
, DBBlobRelationName
,
222 0, (CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*)42,
223 0, (CSSM_DB_SCHEMA_INDEX_INFO
*)42);
225 // @@@ Only iff not already in mDbInfo
226 createRelation(CSSM_DL_DB_RECORD_PUBLIC_KEY
, "CSSM_DL_DB_RECORD_PUBLIC_KEY",
227 KeySchema::KeySchemaAttributeCount
, KeySchema::KeySchemaAttributeList
,
228 KeySchema::KeySchemaIndexCount
, KeySchema::KeySchemaIndexList
);
230 // @@@ Only iff not already in mDbInfo
231 createRelation(CSSM_DL_DB_RECORD_PRIVATE_KEY
, "CSSM_DL_DB_RECORD_PRIVATE_KEY",
232 KeySchema::KeySchemaAttributeCount
, KeySchema::KeySchemaAttributeList
,
233 KeySchema::KeySchemaIndexCount
, KeySchema::KeySchemaIndexList
);
235 // @@@ Only iff not already in mDbInfo
236 createRelation(CSSM_DL_DB_RECORD_SYMMETRIC_KEY
, "CSSM_DL_DB_RECORD_SYMMETRIC_KEY",
237 KeySchema::KeySchemaAttributeCount
, KeySchema::KeySchemaAttributeList
,
238 KeySchema::KeySchemaIndexCount
, KeySchema::KeySchemaIndexList
);
242 SSDatabaseImpl::ssCreate(const DLDbIdentifier
&dlDbIdentifier
)
247 commonCreate(dlDbIdentifier
, autoCommit
);
249 DBParameters dbParameters
;
250 memset(&dbParameters
, 0, sizeof(DBParameters
));
251 dbParameters
.idleTimeout
= kDefaultIdleTimeout
;
252 dbParameters
.lockOnSleep
= kDefaultLockOnSleep
;
254 const AccessCredentials
*cred
= NULL
;
255 const AclEntryInput
*owner
= NULL
;
256 if (resourceControlContext())
258 cred
= AccessCredentials::overlay(resourceControlContext()->AccessCred
);
259 owner
= &AclEntryInput::overlay(resourceControlContext()->InitialAclEntry
);
261 mSSDbHandle
= mClientSession
.createDb(dlDbIdentifier
, cred
, owner
, dbParameters
);
262 CssmDataContainer
dbb(allocator());
263 mClientSession
.encodeDb(mSSDbHandle
, dbb
, allocator());
264 secnotice("integrity", "opening %s", name());
265 Db::Impl::insert(DBBlobRelationID
, NULL
, &dbb
);
268 passThrough(CSSM_APPLEFILEDL_COMMIT
, NULL
);
269 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
,
270 reinterpret_cast<const void *>(1));
275 if (e
.error
!= CSSMERR_DL_DATASTORE_ALREADY_EXISTS
)
289 SSDatabaseImpl::ssCreateWithBlob(const DLDbIdentifier
&dlDbIdentifier
, const CSSM_DATA
&blob
)
294 commonCreate(dlDbIdentifier
, autoCommit
);
295 secnotice("integrity", "opening %s", name());
296 Db::Impl::insert(DBBlobRelationID
, NULL
, &blob
);
299 passThrough(CSSM_APPLEFILEDL_COMMIT
, NULL
);
300 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
,
301 reinterpret_cast<const void *>(1));
312 SSDatabaseImpl::ssOpen(const DLDbIdentifier
&dlDbIdentifier
)
314 load(dlDbIdentifier
);
316 CssmDataContainer
dbb(allocator());
319 // Pull our version out of the database blob
320 mSSDbHandle
= mClientSession
.decodeDb(dlDbIdentifier
, AccessCredentials::overlay(accessCredentials()), dbb
);
324 SSDatabaseImpl::load(const DLDbIdentifier
&dlDbIdentifier
) {
325 mIdentifier
= dlDbIdentifier
;
328 CssmDataContainer
dbb(allocator());
331 secinfo("integrity", "loading %s", name());
335 SSDatabaseImpl::ssRecode(const CssmData
&dbHandleArray
, const CssmData
&agentData
)
337 // Start a transaction (Implies activate()).
338 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
, 0);
342 CssmDataContainer
dbb(allocator());
343 // Make sure mSSDbHandle is valid.
344 CssmClient::DbUniqueRecord dbBlobId
= getDbBlobId(&dbb
);
346 // re-establish the dbHandle with the SecurityServer
347 mSSDbHandle
= mClientSession
.decodeDb(mIdentifier
,
348 AccessCredentials::overlay(accessCredentials()), dbb
);
352 DbHandle successfulHdl
= mClientSession
.authenticateDbsForSync(dbHandleArray
, agentData
);
354 // Create a newDbHandle using the master secrets from the dbBlob we are
356 SecurityServer::DbHandle clonedDbHandle
=
357 mClientSession
.recodeDbForSync(successfulHdl
, mSSDbHandle
);
359 // @@@ If the dbb changed since we fetched it we should abort or
360 // retry the operation here.
362 recodeHelper(clonedDbHandle
, dbBlobId
);
364 // Commit the transaction to the db
365 passThrough(CSSM_APPLEFILEDL_COMMIT
, NULL
);
366 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
,
367 reinterpret_cast<const void *>(1));
371 // Something went wrong rollback the transaction
372 passThrough(CSSM_APPLEFILEDL_ROLLBACK
, NULL
);
373 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT
,
374 reinterpret_cast<const void *>(1));
380 SSDatabaseImpl::recodeDbToVersion(uint32 newBlobVersion
) {
381 // Start a transaction (Implies activate()).
382 DLTransaction
transaction(handle());
387 secnotice("integrity", "is currently locked");
389 secnotice("integrity", "is already unlocked");
392 CssmDataContainer
dbb(allocator());
393 // Make sure mSSDbHandle is valid.
394 CssmClient::DbUniqueRecord dbBlobId
= getDbBlobId(&dbb
);
396 // always establish the dbHandle with the SecurityServer
397 mSSDbHandle
= mClientSession
.decodeDb(mIdentifier
, AccessCredentials::overlay(accessCredentials()), dbb
);
400 // Create a newDbHandle using the master secrets from the dbBlob we are recoding to.
401 secnotice("integrity", "recoding db with handle %d", mSSDbHandle
);
402 SecurityServer::DbHandle clonedDbHandle
= mClientSession
.recodeDbToVersion(newBlobVersion
, mSSDbHandle
);
403 secnotice("integrity", "received db with handle %d", clonedDbHandle
);
405 // @@@ If the dbb changed since we fetched it we should abort or
406 // retry the operation here.
408 uint32 newBlobVersion
= recodeHelper(clonedDbHandle
, dbBlobId
);
409 secnotice("integrity", "committing transaction %d", clonedDbHandle
);
411 // Commit the transaction to the db
412 transaction
.commit();
413 return newBlobVersion
;
422 SSDatabaseImpl::recodeFinished() {
423 mClientSession
.recodeFinished(mSSDbHandle
);
426 void SSDatabaseImpl::takeFileLock() {
428 // you're already in the middle of a file lock.
431 mTransaction
= new DLTransaction(handle());
432 passThrough(CSSM_APPLEFILEDL_TAKE_FILE_LOCK
, NULL
, NULL
);
435 void SSDatabaseImpl::releaseFileLock(bool success
) {
439 mTransaction
->commit();
441 // If we didn't commit, the destructor will roll back and re-enable autocommit
451 void SSDatabaseImpl::makeBackup() {
452 passThrough(CSSM_APPLEFILEDL_MAKE_BACKUP
, NULL
, NULL
);
455 void SSDatabaseImpl::makeCopy(const char* path
) {
456 passThrough(CSSM_APPLEFILEDL_MAKE_COPY
, path
, NULL
);
459 void SSDatabaseImpl::deleteFile() {
460 passThrough(CSSM_APPLEFILEDL_DELETE_FILE
, NULL
, NULL
);
463 SSDatabase
SSDatabaseImpl::ssCloneTo(const DLDbIdentifier
& dldbidentifier
) {
464 makeCopy(dldbidentifier
.dbName());
465 SSDatabase
db(mClientSession
, dl(), dldbidentifier
.dbName(), dldbidentifier
.dbLocation());
467 db
->load(dldbidentifier
);
468 db
->mSSDbHandle
= mClientSession
.cloneDb(dldbidentifier
, mSSDbHandle
);
475 uint32
SSDatabaseImpl::recodeHelper(SecurityServer::DbHandle clonedDbHandle
, CssmClient::DbUniqueRecord
& dbBlobId
) {
477 DbCursor
cursor(SSDatabase(this));
478 cursor
->recordType(CSSM_DL_DB_RECORD_ALL_KEYS
);
479 CssmDataContainer
keyBlob(allocator());
480 CssmClient::DbUniqueRecord keyBlobId
;
481 DbAttributes attributes
;
482 while (cursor
->next(&attributes
, &keyBlob
, keyBlobId
))
484 KeyHandle keyHandle
= 0;
486 // Decode the old key
487 CssmKey::Header header
;
488 keyHandle
= mClientSession
.decodeKey(mSSDbHandle
, keyBlob
, header
);
490 CssmDataContainer
newKeyBlob(mClientSession
.returnAllocator
);
491 mClientSession
.recodeKey(mSSDbHandle
, keyHandle
, clonedDbHandle
, newKeyBlob
);
492 mClientSession
.releaseKey(keyHandle
);
494 // Write the recoded key blob to the database
495 keyBlobId
->modify(attributes
.recordType(), NULL
, &newKeyBlob
,
496 CSSM_DB_MODIFY_ATTRIBUTE_NONE
);
497 } catch (CssmError cssme
) {
498 const char* errStr
= cssmErrorString(cssme
.error
);
499 secnotice("integrity", "corrupt item while recoding: %d %s", (int) cssme
.error
, errStr
);
500 secnotice("integrity", "deleting corrupt item");
503 keyBlobId
->deleteRecord();
506 // tell securityd not to worry about this key again
508 secnotice("integrity", "releasing corrupt key");
509 mClientSession
.releaseKey(keyHandle
);
510 } catch(CssmError cssme
) {
512 const char* errStr
= cssmErrorString(cssme
.error
);
513 secnotice("integrity", "couldn't release corrupt key: %d %s", (int) cssme
.error
, errStr
);
519 // Commit the new blob to securityd, reencode the db blob, release the
520 // cloned db handle and commit the new blob to the db.
521 CssmDataContainer
dbb(allocator());
522 secnotice("integrity", "committing %d", clonedDbHandle
);
523 mClientSession
.commitDbForSync(mSSDbHandle
, clonedDbHandle
,
525 dbBlobId
->modify(DBBlobRelationID
, NULL
, &dbb
,
526 CSSM_DB_MODIFY_ATTRIBUTE_NONE
);
527 return getDbVersionFromBlob(dbb
);
531 void SSDatabaseImpl::getRecordIdentifier(CSSM_DB_UNIQUE_RECORD_PTR uniqueRecord
, CSSM_DATA
&recordID
)
533 // the unique ID is composed of three uint32s (plus one filler word). Pull
534 // them out and byte swap them
535 recordID
.Length
= sizeof (uint32
) * kNumIDWords
;
536 recordID
.Data
= (uint8
*) allocator().malloc(recordID
.Length
);
539 uint32
* dest
= (uint32
*) recordID
.Data
;
540 uint32
* src
= (uint32
*) uniqueRecord
->RecordIdentifier
.Data
;
542 dest
[0] = htonl (src
[0]);
543 dest
[1] = htonl (src
[1]);
544 dest
[2] = htonl (src
[2]);
548 void SSDatabaseImpl::ssCopyBlob(CSSM_DATA
& data
)
550 // get the blob from the database
551 CssmDataContainer
dbb(allocator());
554 // copy the data back
555 data
.Data
= dbb
.Data
;
556 data
.Length
= dbb
.Length
;
558 // zap the return structure so that we don't get zapped when dbb goes out of scope...
564 SSDatabaseImpl::dbBlobVersion() {
565 CssmDataContainer
dbb(allocator());
567 return getDbVersionFromBlob(dbb
);
571 SSDatabaseImpl::getDbVersionFromBlob(const CssmData
& dbb
) {
572 DbBlob
* x
= Allocator::standard().malloc
<DbBlob
>(dbb
.length());
573 memcpy(x
, dbb
, dbb
.length());
574 uint32 version
= x
->version();
575 Allocator::standard().free(x
);
580 SSDatabaseImpl::newDbUniqueRecord()
582 return new SSUniqueRecordImpl(SSDatabase(this));
585 CssmClient::DbUniqueRecord
586 SSDatabaseImpl::getDbBlobId(CssmDataContainer
*dbb
)
588 CssmClient::DbUniqueRecord dbBlobId
;
590 DbCursor
cursor(SSDatabase(this));
591 cursor
->recordType(DBBlobRelationID
);
592 if (!cursor
->next(NULL
, dbb
, dbBlobId
))
593 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT
);
600 SSUniqueRecordImpl::SSUniqueRecordImpl(const SSDatabase
&db
)
601 : DbUniqueRecord::Impl(db
)
605 SSUniqueRecordImpl::~SSUniqueRecordImpl()
610 SSUniqueRecordImpl::database() const
612 return parent
<SSDatabase
>();