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