]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_cspdl/lib/SSDatabase.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_apple_cspdl / lib / SSDatabase.cpp
1 /*
2 * Copyright (c) 2000-2001,2011-2014 Apple 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
26 using namespace CssmClient;
27 using namespace SecurityServer;
28
29 const char *const SSDatabaseImpl::DBBlobRelationName = "DBBlob";
30
31
32 SSDatabaseImpl::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
38 SSDatabaseImpl::~SSDatabaseImpl()
39 try
40 {
41 if (mSSDbHandle != noDb)
42 mClientSession.releaseDb(mSSDbHandle);
43 }
44 catch (...)
45 {
46 }
47
48 SSUniqueRecord
49 SSDatabaseImpl::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
62 void
63 SSDatabaseImpl::authenticate(CSSM_DB_ACCESS_TYPE inAccessRequest,
64 const CSSM_ACCESS_CREDENTIALS *inAccessCredentials)
65 {
66 mClientSession.authenticateDb(dbHandle(), inAccessRequest,
67 AccessCredentials::overlay(inAccessCredentials));
68 }
69
70 void
71 SSDatabaseImpl::lock()
72 {
73 mClientSession.lock(dbHandle());
74
75 }
76
77 void
78 SSDatabaseImpl::unlock()
79 {
80 mClientSession.unlock(dbHandle());
81 }
82
83 void
84 SSDatabaseImpl::unlock(const CSSM_DATA &password)
85 {
86 mClientSession.unlock(dbHandle(), CssmData::overlay(password));
87 }
88
89 void
90 SSDatabaseImpl::stash()
91 {
92 mClientSession.stashDb(dbHandle());
93 }
94
95 void
96 SSDatabaseImpl::stashCheck()
97 {
98 mClientSession.stashDbCheck(dbHandle());
99 }
100
101 void
102 SSDatabaseImpl::getSettings(uint32 &outIdleTimeout, bool &outLockOnSleep)
103 {
104 DBParameters parameters;
105 mClientSession.getDbParameters(dbHandle(), parameters);
106 outIdleTimeout = parameters.idleTimeout;
107 outLockOnSleep = parameters.lockOnSleep;
108 }
109
110 void
111 SSDatabaseImpl::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
124 bool
125 SSDatabaseImpl::isLocked()
126 {
127 return mClientSession.isLocked(dbHandle());
128 }
129
130 void
131 SSDatabaseImpl::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
141 DbHandle
142 SSDatabaseImpl::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
155 void
156 SSDatabaseImpl::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
237 void
238 SSDatabaseImpl::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
283 void
284 SSDatabaseImpl::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
305 void
306 SSDatabaseImpl::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
317 void
318 SSDatabaseImpl::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
389 void 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
406 void 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
421 DbUniqueRecordImpl *
422 SSDatabaseImpl::newDbUniqueRecord()
423 {
424 return new SSUniqueRecordImpl(SSDatabase(this));
425 }
426
427 CssmClient::DbUniqueRecord
428 SSDatabaseImpl::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
442 SSUniqueRecordImpl::SSUniqueRecordImpl(const SSDatabase &db)
443 : DbUniqueRecord::Impl(db)
444 {
445 }
446
447 SSUniqueRecordImpl::~SSUniqueRecordImpl()
448 {
449 }
450
451 SSDatabase
452 SSUniqueRecordImpl::database() const
453 {
454 return parent<SSDatabase>();
455 }