]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_cspdl/lib/SSDatabase.cpp
Security-57337.40.85.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 #include <security_utilities/CSPDLTransaction.h>
26 #include <Security/SecBasePriv.h>
27
28 using namespace CssmClient;
29 using namespace SecurityServer;
30
31 const char *const SSDatabaseImpl::DBBlobRelationName = "DBBlob";
32
33
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)
37 {
38 mTransaction = NULL;
39 }
40
41 SSDatabaseImpl::~SSDatabaseImpl()
42 try
43 {
44 if (mSSDbHandle != noDb)
45 mClientSession.releaseDb(mSSDbHandle);
46 }
47 catch (...)
48 {
49 }
50
51 SSUniqueRecord
52 SSDatabaseImpl::insert(CSSM_DB_RECORDTYPE recordType,
53 const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes,
54 const CSSM_DATA *data, bool)
55 {
56 SSUniqueRecord uniqueId(SSDatabase(this));
57 check(CSSM_DL_DataInsert(handle(), recordType,
58 attributes,
59 data, uniqueId));
60 // Activate uniqueId so CSSM_DL_FreeUniqueRecord() gets called when it goes out of scope.
61 uniqueId->activate();
62 return uniqueId;
63 }
64
65 void
66 SSDatabaseImpl::authenticate(CSSM_DB_ACCESS_TYPE inAccessRequest,
67 const CSSM_ACCESS_CREDENTIALS *inAccessCredentials)
68 {
69 mClientSession.authenticateDb(dbHandle(), inAccessRequest,
70 AccessCredentials::overlay(inAccessCredentials));
71 }
72
73 void
74 SSDatabaseImpl::lock()
75 {
76 mClientSession.lock(dbHandle());
77
78 }
79
80 void
81 SSDatabaseImpl::unlock()
82 {
83 mClientSession.unlock(dbHandle());
84 }
85
86 void
87 SSDatabaseImpl::unlock(const CSSM_DATA &password)
88 {
89 mClientSession.unlock(dbHandle(), CssmData::overlay(password));
90 }
91
92 void
93 SSDatabaseImpl::stash()
94 {
95 mClientSession.stashDb(dbHandle());
96 }
97
98 void
99 SSDatabaseImpl::stashCheck()
100 {
101 mClientSession.stashDbCheck(dbHandle());
102 }
103
104 void
105 SSDatabaseImpl::getSettings(uint32 &outIdleTimeout, bool &outLockOnSleep)
106 {
107 DBParameters parameters;
108 mClientSession.getDbParameters(dbHandle(), parameters);
109 outIdleTimeout = parameters.idleTimeout;
110 outLockOnSleep = parameters.lockOnSleep;
111 }
112
113 void
114 SSDatabaseImpl::setSettings(uint32 inIdleTimeout, bool inLockOnSleep)
115 {
116 DBParameters parameters;
117 parameters.idleTimeout = inIdleTimeout;
118 parameters.lockOnSleep = inLockOnSleep;
119 mClientSession.setDbParameters(dbHandle(), parameters);
120
121 // Reencode the db blob.
122 CssmDataContainer dbb(allocator());
123 mClientSession.encodeDb(mSSDbHandle, dbb, allocator());
124 getDbBlobId()->modify(DBBlobRelationID, NULL, &dbb, CSSM_DB_MODIFY_ATTRIBUTE_NONE);
125 }
126
127 bool
128 SSDatabaseImpl::isLocked()
129 {
130 return mClientSession.isLocked(dbHandle());
131 }
132
133 void
134 SSDatabaseImpl::changePassphrase(const CSSM_ACCESS_CREDENTIALS *cred)
135 {
136 mClientSession.changePassphrase(dbHandle(), AccessCredentials::overlay(cred));
137
138 // Reencode the db blob.
139 CssmDataContainer dbb(allocator());
140 mClientSession.encodeDb(mSSDbHandle, dbb, allocator());
141 getDbBlobId()->modify(DBBlobRelationID, NULL, &dbb, CSSM_DB_MODIFY_ATTRIBUTE_NONE);
142 }
143
144 DbHandle
145 SSDatabaseImpl::dbHandle()
146 {
147 activate();
148 if (mForked()) {
149 // re-establish the dbHandle with the SecurityServer
150 CssmDataContainer dbb(allocator());
151 getDbBlobId(&dbb);
152 mSSDbHandle = mClientSession.decodeDb(mIdentifier,
153 AccessCredentials::overlay(accessCredentials()), dbb);
154 }
155 return mSSDbHandle;
156 }
157
158 void
159 SSDatabaseImpl::commonCreate(const DLDbIdentifier &dlDbIdentifier, bool &autoCommit)
160 {
161 mIdentifier = dlDbIdentifier;
162 // Set to false if autocommit should remain off after the create.
163 autoCommit = true;
164
165 // OpenParameters to use
166 CSSM_APPLEDL_OPEN_PARAMETERS newOpenParameters =
167 {
168 sizeof(CSSM_APPLEDL_OPEN_PARAMETERS),
169 CSSM_APPLEDL_OPEN_PARAMETERS_VERSION,
170 CSSM_FALSE, // do not auto-commit
171 0 // mask - do not use following fields
172 };
173
174 // Get the original openParameters and apply them to the ones we
175 // are passing in.
176 const CSSM_APPLEDL_OPEN_PARAMETERS *inOpenParameters =
177 reinterpret_cast<const CSSM_APPLEDL_OPEN_PARAMETERS *>(openParameters());
178 if (inOpenParameters)
179 {
180 switch (inOpenParameters->version)
181 {
182 case 1:
183 if (inOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS))
184 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
185
186 newOpenParameters.mask = inOpenParameters->mask;
187 newOpenParameters.mode = inOpenParameters->mode;
188 /*DROPTHROUGH*/
189 case 0:
190 //if (inOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0))
191 // CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
192
193 // This will determine whether we leave autocommit off or not.
194 autoCommit = inOpenParameters->autoCommit == CSSM_FALSE ? false : true;
195 break;
196
197 default:
198 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
199 }
200 }
201
202 // Use the new openParameters
203 openParameters(&newOpenParameters);
204 try
205 {
206 DbImpl::create();
207 // Restore the original openparameters again.
208 openParameters(inOpenParameters);
209 }
210 catch (...)
211 {
212 // Make sure restore the original openparameters again even if
213 // create throws.
214 openParameters(inOpenParameters);
215 throw;
216 }
217
218 // @@@ The CSSM_DB_SCHEMA_ATTRIBUTE_INFO and CSSM_DB_SCHEMA_INDEX_INFO
219 // arguments should be optional.
220 createRelation(DBBlobRelationID, DBBlobRelationName,
221 0, (CSSM_DB_SCHEMA_ATTRIBUTE_INFO *)42,
222 0, (CSSM_DB_SCHEMA_INDEX_INFO *)42);
223
224 // @@@ Only iff not already in mDbInfo
225 createRelation(CSSM_DL_DB_RECORD_PUBLIC_KEY, "CSSM_DL_DB_RECORD_PUBLIC_KEY",
226 KeySchema::KeySchemaAttributeCount, KeySchema::KeySchemaAttributeList,
227 KeySchema::KeySchemaIndexCount, KeySchema::KeySchemaIndexList);
228
229 // @@@ Only iff not already in mDbInfo
230 createRelation(CSSM_DL_DB_RECORD_PRIVATE_KEY, "CSSM_DL_DB_RECORD_PRIVATE_KEY",
231 KeySchema::KeySchemaAttributeCount, KeySchema::KeySchemaAttributeList,
232 KeySchema::KeySchemaIndexCount, KeySchema::KeySchemaIndexList);
233
234 // @@@ Only iff not already in mDbInfo
235 createRelation(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, "CSSM_DL_DB_RECORD_SYMMETRIC_KEY",
236 KeySchema::KeySchemaAttributeCount, KeySchema::KeySchemaAttributeList,
237 KeySchema::KeySchemaIndexCount, KeySchema::KeySchemaIndexList);
238 }
239
240 void
241 SSDatabaseImpl::create(const DLDbIdentifier &dlDbIdentifier)
242 {
243 try
244 {
245 bool autoCommit;
246 commonCreate(dlDbIdentifier, autoCommit);
247
248 DBParameters dbParameters;
249 memset(&dbParameters, 0, sizeof(DBParameters));
250 dbParameters.idleTimeout = kDefaultIdleTimeout;
251 dbParameters.lockOnSleep = kDefaultLockOnSleep;
252
253 const AccessCredentials *cred = NULL;
254 const AclEntryInput *owner = NULL;
255 if (resourceControlContext())
256 {
257 cred = AccessCredentials::overlay(resourceControlContext()->AccessCred);
258 owner = &AclEntryInput::overlay(resourceControlContext()->InitialAclEntry);
259 }
260 mSSDbHandle = mClientSession.createDb(dlDbIdentifier, cred, owner, dbParameters);
261 CssmDataContainer dbb(allocator());
262 mClientSession.encodeDb(mSSDbHandle, dbb, allocator());
263 secdebugfunc("integrity", "opening %s", name());
264 Db::Impl::insert(DBBlobRelationID, NULL, &dbb);
265 if (autoCommit)
266 {
267 passThrough(CSSM_APPLEFILEDL_COMMIT, NULL);
268 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
269 reinterpret_cast<const void *>(1));
270 }
271 }
272 catch(CssmError e)
273 {
274 if (e.error != CSSMERR_DL_DATASTORE_ALREADY_EXISTS)
275 {
276 DbImpl::deleteDb();
277 }
278 throw;
279 }
280 catch(...)
281 {
282 DbImpl::deleteDb();
283 throw;
284 }
285 }
286
287 void
288 SSDatabaseImpl::createWithBlob(const DLDbIdentifier &dlDbIdentifier, const CSSM_DATA &blob)
289 {
290 try
291 {
292 bool autoCommit;
293 commonCreate(dlDbIdentifier, autoCommit);
294 secdebugfunc("integrity", "opening %s", name());
295 Db::Impl::insert(DBBlobRelationID, NULL, &blob);
296 if (autoCommit)
297 {
298 passThrough(CSSM_APPLEFILEDL_COMMIT, NULL);
299 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
300 reinterpret_cast<const void *>(1));
301 }
302 }
303 catch(...)
304 {
305 DbImpl::deleteDb();
306 throw;
307 }
308 }
309
310 void
311 SSDatabaseImpl::open(const DLDbIdentifier &dlDbIdentifier)
312 {
313 mIdentifier = dlDbIdentifier;
314 Db::Impl::open();
315
316 CssmDataContainer dbb(allocator());
317 getDbBlobId(&dbb);
318
319 secdebugfunc("integrity", "opening %s", name());
320
321 // Pull our version out of the database blob
322 mSSDbHandle = mClientSession.decodeDb(dlDbIdentifier, AccessCredentials::overlay(accessCredentials()), dbb);
323 }
324
325 void
326 SSDatabaseImpl::recode(const CssmData &dbHandleArray, const CssmData &agentData)
327 {
328 // Start a transaction (Implies activate()).
329 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 0);
330
331 try
332 {
333 CssmDataContainer dbb(allocator());
334 // Make sure mSSDbHandle is valid.
335 CssmClient::DbUniqueRecord dbBlobId = getDbBlobId(&dbb);
336 if (mForked()) {
337 // re-establish the dbHandle with the SecurityServer
338 mSSDbHandle = mClientSession.decodeDb(mIdentifier,
339 AccessCredentials::overlay(accessCredentials()), dbb);
340 }
341 dbb.clear();
342
343 DbHandle successfulHdl = mClientSession.authenticateDbsForSync(dbHandleArray, agentData);
344
345 // Create a newDbHandle using the master secrets from the dbBlob we are
346 // recoding to.
347 SecurityServer::DbHandle clonedDbHandle =
348 mClientSession.recodeDbForSync(successfulHdl, mSSDbHandle);
349
350 // @@@ If the dbb changed since we fetched it we should abort or
351 // retry the operation here.
352
353 recodeHelper(clonedDbHandle, dbBlobId);
354
355 // Commit the transaction to the db
356 passThrough(CSSM_APPLEFILEDL_COMMIT, NULL);
357 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
358 reinterpret_cast<const void *>(1));
359 }
360 catch (...)
361 {
362 // Something went wrong rollback the transaction
363 passThrough(CSSM_APPLEFILEDL_ROLLBACK, NULL);
364 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
365 reinterpret_cast<const void *>(1));
366 throw;
367 }
368 }
369
370 uint32
371 SSDatabaseImpl::recodeDbToVersion(uint32 newBlobVersion) {
372 // Start a transaction (Implies activate()).
373 DLTransaction transaction(handle());
374
375 try
376 {
377 if(isLocked()) {
378 secdebugfunc("integrity", "is currently locked");
379 } else {
380 secdebugfunc("integrity", "is already unlocked");
381 }
382
383 CssmDataContainer dbb(allocator());
384 // Make sure mSSDbHandle is valid.
385 CssmClient::DbUniqueRecord dbBlobId = getDbBlobId(&dbb);
386
387 // always establish the dbHandle with the SecurityServer
388 mSSDbHandle = mClientSession.decodeDb(mIdentifier, AccessCredentials::overlay(accessCredentials()), dbb);
389 dbb.clear();
390
391 // Create a newDbHandle using the master secrets from the dbBlob we are recoding to.
392 secdebugfunc("integrity", "recoding db with handle %d", mSSDbHandle);
393 SecurityServer::DbHandle clonedDbHandle = mClientSession.recodeDbToVersion(newBlobVersion, mSSDbHandle);
394 secdebugfunc("integrity", "received db with handle %d", clonedDbHandle);
395
396 // @@@ If the dbb changed since we fetched it we should abort or
397 // retry the operation here.
398
399 uint32 newBlobVersion = recodeHelper(clonedDbHandle, dbBlobId);
400 secdebugfunc("integrity", "committing transaction %d", clonedDbHandle);
401
402 // Commit the transaction to the db
403 transaction.success();
404 return newBlobVersion;
405 }
406 catch (...)
407 {
408 throw;
409 }
410 }
411
412 void SSDatabaseImpl::takeFileLock() {
413 if(mTransaction) {
414 // you're already in the middle of a file lock.
415 return;
416 }
417 mTransaction = new DLTransaction(handle());
418 passThrough(CSSM_APPLEFILEDL_TAKE_FILE_LOCK, NULL, NULL);
419 }
420
421 void SSDatabaseImpl::releaseFileLock(bool success) {
422 if(mTransaction) {
423 try {
424 if(success) {
425 mTransaction->success();
426 }
427 // The destructor will commit the database and re-enable autocommit (if needed)
428 delete mTransaction;
429 mTransaction = NULL;
430 } catch(...) {
431 mTransaction = NULL;
432 throw;
433 }
434 }
435 }
436
437 void SSDatabaseImpl::makeBackup() {
438 passThrough(CSSM_APPLEFILEDL_MAKE_BACKUP, NULL, NULL);
439 }
440
441
442 uint32 SSDatabaseImpl::recodeHelper(SecurityServer::DbHandle clonedDbHandle, CssmClient::DbUniqueRecord& dbBlobId) {
443 // Recode all keys
444 DbCursor cursor(SSDatabase(this));
445 cursor->recordType(CSSM_DL_DB_RECORD_ALL_KEYS);
446 CssmDataContainer keyBlob(allocator());
447 CssmClient::DbUniqueRecord keyBlobId;
448 DbAttributes attributes;
449 while (cursor->next(&attributes, &keyBlob, keyBlobId))
450 {
451 KeyHandle keyHandle = 0;
452 try {
453 // Decode the old key
454 CssmKey::Header header;
455 keyHandle = mClientSession.decodeKey(mSSDbHandle, keyBlob, header);
456 // Recode the key
457 CssmDataContainer newKeyBlob(mClientSession.returnAllocator);
458 mClientSession.recodeKey(mSSDbHandle, keyHandle, clonedDbHandle, newKeyBlob);
459 mClientSession.releaseKey(keyHandle);
460 keyHandle = 0;
461 // Write the recoded key blob to the database
462 keyBlobId->modify(attributes.recordType(), NULL, &newKeyBlob,
463 CSSM_DB_MODIFY_ATTRIBUTE_NONE);
464 } catch (CssmError cssme) {
465 const char* errStr = cssmErrorString(cssme.error);
466 secdebugfunc("integrity", "corrupt item while recoding: %d %s", (int) cssme.error, errStr);
467 secdebugfunc("integrity", "deleting corrupt item");
468
469
470 keyBlobId->deleteRecord();
471
472 if(keyHandle != 0) {
473 // tell securityd not to worry about this key again
474 try {
475 secdebugfunc("integrity", "releasing corrupt key");
476 mClientSession.releaseKey(keyHandle);
477 } catch(CssmError cssme) {
478 // swallow the error
479 const char* errStr = cssmErrorString(cssme.error);
480 secdebugfunc("integrity", "couldn't release corrupt key: %d %s", (int) cssme.error, errStr);
481 }
482 }
483 }
484 }
485
486 // Commit the new blob to securityd, reencode the db blob, release the
487 // cloned db handle and commit the new blob to the db.
488 CssmDataContainer dbb(allocator());
489 secdebugfunc("integrity", "committing %d", clonedDbHandle);
490 mClientSession.commitDbForSync(mSSDbHandle, clonedDbHandle,
491 dbb, allocator());
492 dbBlobId->modify(DBBlobRelationID, NULL, &dbb,
493 CSSM_DB_MODIFY_ATTRIBUTE_NONE);
494 return getDbVersionFromBlob(dbb);
495 }
496
497
498 void SSDatabaseImpl::getRecordIdentifier(CSSM_DB_UNIQUE_RECORD_PTR uniqueRecord, CSSM_DATA &recordID)
499 {
500 // the unique ID is composed of three uint32s (plus one filler word). Pull
501 // them out and byte swap them
502 recordID.Length = sizeof (uint32) * kNumIDWords;
503 recordID.Data = (uint8*) allocator().malloc(recordID.Length);
504
505 // copy the data
506 uint32* dest = (uint32*) recordID.Data;
507 uint32* src = (uint32*) uniqueRecord->RecordIdentifier.Data;
508
509 dest[0] = htonl (src[0]);
510 dest[1] = htonl (src[1]);
511 dest[2] = htonl (src[2]);
512 dest[3] = 0;
513 }
514
515 void SSDatabaseImpl::copyBlob(CSSM_DATA &data)
516 {
517 // get the blob from the database
518 CssmDataContainer dbb(allocator());
519 getDbBlobId(&dbb);
520
521 // copy the data back
522 data.Data = dbb.Data;
523 data.Length = dbb.Length;
524
525 // zap the return structure so that we don't get zapped when dbb goes out of scope...
526 dbb.Data = NULL;
527 dbb.Length = 0;
528 }
529
530 uint32
531 SSDatabaseImpl::dbBlobVersion() {
532 CssmDataContainer dbb(allocator());
533 getDbBlobId(&dbb);
534 return getDbVersionFromBlob(dbb);
535 }
536
537 uint32
538 SSDatabaseImpl::getDbVersionFromBlob(const CssmData& dbb) {
539 DbBlob* x = Allocator::standard().malloc<DbBlob>(dbb.length());
540 memcpy(x, dbb, dbb.length());
541 uint32 version = x->version();
542 Allocator::standard().free(x);
543 return version;
544 }
545
546 DbUniqueRecordImpl *
547 SSDatabaseImpl::newDbUniqueRecord()
548 {
549 return new SSUniqueRecordImpl(SSDatabase(this));
550 }
551
552 CssmClient::DbUniqueRecord
553 SSDatabaseImpl::getDbBlobId(CssmDataContainer *dbb)
554 {
555 CssmClient::DbUniqueRecord dbBlobId;
556
557 DbCursor cursor(SSDatabase(this));
558 cursor->recordType(DBBlobRelationID);
559 if (!cursor->next(NULL, dbb, dbBlobId))
560 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
561
562 return dbBlobId;
563 }
564
565
566
567 SSUniqueRecordImpl::SSUniqueRecordImpl(const SSDatabase &db)
568 : DbUniqueRecord::Impl(db)
569 {
570 }
571
572 SSUniqueRecordImpl::~SSUniqueRecordImpl()
573 {
574 }
575
576 SSDatabase
577 SSUniqueRecordImpl::database() const
578 {
579 return parent<SSDatabase>();
580 }