]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_cspdl/lib/SSDatabase.cpp
Security-59754.80.3.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 return; // Prevent re-throw of exception [function-try-block]
50 }
51
52 SSUniqueRecord
53 SSDatabaseImpl::ssInsert(CSSM_DB_RECORDTYPE recordType,
54 const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes,
55 const CSSM_DATA *data)
56 {
57 SSUniqueRecord uniqueId(SSDatabase(this));
58 check(CSSM_DL_DataInsert(handle(), recordType,
59 attributes,
60 data, uniqueId));
61 // Activate uniqueId so CSSM_DL_FreeUniqueRecord() gets called when it goes out of scope.
62 uniqueId->activate();
63 return uniqueId;
64 }
65
66 void
67 SSDatabaseImpl::authenticate(CSSM_DB_ACCESS_TYPE inAccessRequest,
68 const CSSM_ACCESS_CREDENTIALS *inAccessCredentials)
69 {
70 mClientSession.authenticateDb(dbHandle(), inAccessRequest,
71 AccessCredentials::overlay(inAccessCredentials));
72 }
73
74 void
75 SSDatabaseImpl::lock()
76 {
77 mClientSession.lock(dbHandle());
78
79 }
80
81 void
82 SSDatabaseImpl::unlock()
83 {
84 mClientSession.unlock(dbHandle());
85 }
86
87 void
88 SSDatabaseImpl::unlock(const CSSM_DATA &password)
89 {
90 mClientSession.unlock(dbHandle(), CssmData::overlay(password));
91 }
92
93 void
94 SSDatabaseImpl::stash()
95 {
96 mClientSession.stashDb(dbHandle());
97 }
98
99 void
100 SSDatabaseImpl::stashCheck()
101 {
102 mClientSession.stashDbCheck(dbHandle());
103 }
104
105 void
106 SSDatabaseImpl::getSettings(uint32 &outIdleTimeout, bool &outLockOnSleep)
107 {
108 DBParameters parameters;
109 mClientSession.getDbParameters(dbHandle(), parameters);
110 outIdleTimeout = parameters.idleTimeout;
111 outLockOnSleep = parameters.lockOnSleep;
112 }
113
114 void
115 SSDatabaseImpl::setSettings(uint32 inIdleTimeout, bool inLockOnSleep)
116 {
117 DBParameters parameters;
118 parameters.idleTimeout = inIdleTimeout;
119 parameters.lockOnSleep = inLockOnSleep;
120 mClientSession.setDbParameters(dbHandle(), parameters);
121
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);
126 }
127
128 bool
129 SSDatabaseImpl::isLocked()
130 {
131 return mClientSession.isLocked(dbHandle());
132 }
133
134 void
135 SSDatabaseImpl::changePassphrase(const CSSM_ACCESS_CREDENTIALS *cred)
136 {
137 mClientSession.changePassphrase(dbHandle(), AccessCredentials::overlay(cred));
138
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);
143 }
144
145 DbHandle
146 SSDatabaseImpl::dbHandle()
147 {
148 activate();
149 if (mForked()) {
150 // re-establish the dbHandle with the SecurityServer
151 CssmDataContainer dbb(allocator());
152 getDbBlobId(&dbb);
153 mSSDbHandle = mClientSession.decodeDb(mIdentifier,
154 AccessCredentials::overlay(accessCredentials()), dbb);
155 }
156 return mSSDbHandle;
157 }
158
159 void
160 SSDatabaseImpl::commonCreate(const DLDbIdentifier &dlDbIdentifier, bool &autoCommit)
161 {
162 mIdentifier = dlDbIdentifier;
163 // Set to false if autocommit should remain off after the create.
164 autoCommit = true;
165
166 // OpenParameters to use
167 CSSM_APPLEDL_OPEN_PARAMETERS newOpenParameters =
168 {
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
173 };
174
175 // Get the original openParameters and apply them to the ones we
176 // are passing in.
177 const CSSM_APPLEDL_OPEN_PARAMETERS *inOpenParameters =
178 reinterpret_cast<const CSSM_APPLEDL_OPEN_PARAMETERS *>(openParameters());
179 if (inOpenParameters)
180 {
181 switch (inOpenParameters->version)
182 {
183 case 1:
184 if (inOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS))
185 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
186
187 newOpenParameters.mask = inOpenParameters->mask;
188 newOpenParameters.mode = inOpenParameters->mode;
189 /*DROPTHROUGH*/
190 case 0:
191 //if (inOpenParameters->length < sizeof(CSSM_APPLEDL_OPEN_PARAMETERS_V0))
192 // CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
193
194 // This will determine whether we leave autocommit off or not.
195 autoCommit = inOpenParameters->autoCommit == CSSM_FALSE ? false : true;
196 break;
197
198 default:
199 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_OPEN_PARAMETERS);
200 }
201 }
202
203 // Use the new openParameters
204 openParameters(&newOpenParameters);
205 try
206 {
207 DbImpl::create();
208 // Restore the original openparameters again.
209 openParameters(inOpenParameters);
210 }
211 catch (...)
212 {
213 // Make sure restore the original openparameters again even if
214 // create throws.
215 openParameters(inOpenParameters);
216 throw;
217 }
218
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);
224
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);
229
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);
234
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);
239 }
240
241 void
242 SSDatabaseImpl::ssCreate(const DLDbIdentifier &dlDbIdentifier)
243 {
244 try
245 {
246 bool autoCommit;
247 commonCreate(dlDbIdentifier, autoCommit);
248
249 DBParameters dbParameters;
250 memset(&dbParameters, 0, sizeof(DBParameters));
251 dbParameters.idleTimeout = kDefaultIdleTimeout;
252 dbParameters.lockOnSleep = kDefaultLockOnSleep;
253
254 const AccessCredentials *cred = NULL;
255 const AclEntryInput *owner = NULL;
256 if (resourceControlContext())
257 {
258 cred = AccessCredentials::overlay(resourceControlContext()->AccessCred);
259 owner = &AclEntryInput::overlay(resourceControlContext()->InitialAclEntry);
260 }
261 mSSDbHandle = mClientSession.createDb(dlDbIdentifier, cred, owner, dbParameters);
262 CssmDataContainer dbb(allocator());
263 mClientSession.encodeDb(mSSDbHandle, dbb, allocator());
264 secinfo("integrity", "opening %s", name());
265 Db::Impl::insert(DBBlobRelationID, NULL, &dbb);
266 if (autoCommit)
267 {
268 passThrough(CSSM_APPLEFILEDL_COMMIT, NULL);
269 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
270 reinterpret_cast<const void *>(1));
271 }
272 }
273 catch(CssmError e)
274 {
275 if (e.error != CSSMERR_DL_DATASTORE_ALREADY_EXISTS)
276 {
277 DbImpl::deleteDb();
278 }
279 throw;
280 }
281 catch(...)
282 {
283 DbImpl::deleteDb();
284 throw;
285 }
286 }
287
288 void
289 SSDatabaseImpl::ssCreateWithBlob(const DLDbIdentifier &dlDbIdentifier, const CSSM_DATA &blob)
290 {
291 try
292 {
293 bool autoCommit;
294 commonCreate(dlDbIdentifier, autoCommit);
295 secinfo("integrity", "opening %s", name());
296 Db::Impl::insert(DBBlobRelationID, NULL, &blob);
297 if (autoCommit)
298 {
299 passThrough(CSSM_APPLEFILEDL_COMMIT, NULL);
300 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
301 reinterpret_cast<const void *>(1));
302 }
303 }
304 catch(...)
305 {
306 DbImpl::deleteDb();
307 throw;
308 }
309 }
310
311 void
312 SSDatabaseImpl::ssOpen(const DLDbIdentifier &dlDbIdentifier)
313 {
314 load(dlDbIdentifier);
315
316 CssmDataContainer dbb(allocator());
317 getDbBlobId(&dbb);
318
319 // Pull our version out of the database blob
320 mSSDbHandle = mClientSession.decodeDb(dlDbIdentifier, AccessCredentials::overlay(accessCredentials()), dbb);
321 }
322
323 void
324 SSDatabaseImpl::load(const DLDbIdentifier &dlDbIdentifier) {
325 mIdentifier = dlDbIdentifier;
326 Db::Impl::open();
327
328 CssmDataContainer dbb(allocator());
329 getDbBlobId(&dbb);
330
331 secinfo("integrity", "loading %s", name());
332 }
333
334 void
335 SSDatabaseImpl::ssRecode(const CssmData &dbHandleArray, const CssmData &agentData)
336 {
337 // Start a transaction (Implies activate()).
338 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 0);
339
340 try
341 {
342 CssmDataContainer dbb(allocator());
343 // Make sure mSSDbHandle is valid.
344 CssmClient::DbUniqueRecord dbBlobId = getDbBlobId(&dbb);
345 if (mForked()) {
346 // re-establish the dbHandle with the SecurityServer
347 mSSDbHandle = mClientSession.decodeDb(mIdentifier,
348 AccessCredentials::overlay(accessCredentials()), dbb);
349 }
350 dbb.clear();
351
352 DbHandle successfulHdl = mClientSession.authenticateDbsForSync(dbHandleArray, agentData);
353
354 // Create a newDbHandle using the master secrets from the dbBlob we are
355 // recoding to.
356 SecurityServer::DbHandle clonedDbHandle =
357 mClientSession.recodeDbForSync(successfulHdl, mSSDbHandle);
358
359 // @@@ If the dbb changed since we fetched it we should abort or
360 // retry the operation here.
361
362 recodeHelper(clonedDbHandle, dbBlobId);
363
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));
368 }
369 catch (...)
370 {
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));
375 throw;
376 }
377 }
378
379 uint32
380 SSDatabaseImpl::recodeDbToVersion(uint32 newBlobVersion) {
381 // Start a transaction (Implies activate()).
382 DLTransaction transaction(handle());
383
384 try
385 {
386 if(isLocked()) {
387 secinfo("integrity", "is currently locked");
388 } else {
389 secinfo("integrity", "is already unlocked");
390 }
391
392 CssmDataContainer dbb(allocator());
393 // Make sure mSSDbHandle is valid.
394 CssmClient::DbUniqueRecord dbBlobId = getDbBlobId(&dbb);
395
396 // always establish the dbHandle with the SecurityServer
397 mSSDbHandle = mClientSession.decodeDb(mIdentifier, AccessCredentials::overlay(accessCredentials()), dbb);
398 dbb.clear();
399
400 // Create a newDbHandle using the master secrets from the dbBlob we are recoding to.
401 secinfo("integrity", "recoding db with handle %d", mSSDbHandle);
402 SecurityServer::DbHandle clonedDbHandle = mClientSession.recodeDbToVersion(newBlobVersion, mSSDbHandle);
403 secinfo("integrity", "received db with handle %d", clonedDbHandle);
404
405 // @@@ If the dbb changed since we fetched it we should abort or
406 // retry the operation here.
407
408 uint32 newBlobVersion = recodeHelper(clonedDbHandle, dbBlobId);
409 secinfo("integrity", "committing transaction %d", clonedDbHandle);
410
411 // Commit the transaction to the db
412 transaction.commit();
413 return newBlobVersion;
414 }
415 catch (...)
416 {
417 throw;
418 }
419 }
420
421 void
422 SSDatabaseImpl::recodeFinished() {
423 mClientSession.recodeFinished(mSSDbHandle);
424 }
425
426 void SSDatabaseImpl::takeFileLock() {
427 if(mTransaction) {
428 // you're already in the middle of a file lock.
429 return;
430 }
431 mTransaction = new DLTransaction(handle());
432 passThrough(CSSM_APPLEFILEDL_TAKE_FILE_LOCK, NULL, NULL);
433 }
434
435 void SSDatabaseImpl::releaseFileLock(bool success) {
436 if(mTransaction) {
437 try {
438 if(success) {
439 mTransaction->commit();
440 }
441 // If we didn't commit, the destructor will roll back and re-enable autocommit
442 delete mTransaction;
443 mTransaction = NULL;
444 } catch(...) {
445 mTransaction = NULL;
446 throw;
447 }
448 }
449 }
450
451 void SSDatabaseImpl::makeBackup() {
452 passThrough(CSSM_APPLEFILEDL_MAKE_BACKUP, NULL, NULL);
453 }
454
455 void SSDatabaseImpl::makeCopy(const char* path) {
456 passThrough(CSSM_APPLEFILEDL_MAKE_COPY, path, NULL);
457 }
458
459 void SSDatabaseImpl::deleteFile() {
460 passThrough(CSSM_APPLEFILEDL_DELETE_FILE, NULL, NULL);
461 }
462
463 SSDatabase SSDatabaseImpl::ssCloneTo(const DLDbIdentifier& dldbidentifier) {
464 makeCopy(dldbidentifier.dbName());
465 SSDatabase db(mClientSession, dl(), dldbidentifier.dbName(), dldbidentifier.dbLocation());
466
467 db->load(dldbidentifier);
468 db->mSSDbHandle = mClientSession.cloneDb(dldbidentifier, mSSDbHandle);
469
470 return db;
471 }
472
473
474
475 uint32 SSDatabaseImpl::recodeHelper(SecurityServer::DbHandle clonedDbHandle, CssmClient::DbUniqueRecord& dbBlobId) {
476 // Recode all keys
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))
483 {
484 KeyHandle keyHandle = 0;
485 try {
486 // Decode the old key
487 CssmKey::Header header;
488 keyHandle = mClientSession.decodeKey(mSSDbHandle, keyBlob, header);
489 // Recode the key
490 CssmDataContainer newKeyBlob(mClientSession.returnAllocator);
491 mClientSession.recodeKey(mSSDbHandle, keyHandle, clonedDbHandle, newKeyBlob);
492 mClientSession.releaseKey(keyHandle);
493 keyHandle = 0;
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");
501
502
503 keyBlobId->deleteRecord();
504
505 if(keyHandle != 0) {
506 // tell securityd not to worry about this key again
507 try {
508 secnotice("integrity", "releasing corrupt key");
509 mClientSession.releaseKey(keyHandle);
510 } catch(CssmError cssme) {
511 // swallow the error
512 const char* errStr = cssmErrorString(cssme.error);
513 secnotice("integrity", "couldn't release corrupt key: %d %s", (int) cssme.error, errStr);
514 }
515 }
516 }
517 }
518
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 secinfo("integrity", "committing %d", clonedDbHandle);
523 mClientSession.commitDbForSync(mSSDbHandle, clonedDbHandle,
524 dbb, allocator());
525 dbBlobId->modify(DBBlobRelationID, NULL, &dbb,
526 CSSM_DB_MODIFY_ATTRIBUTE_NONE);
527 return getDbVersionFromBlob(dbb);
528 }
529
530
531 void SSDatabaseImpl::getRecordIdentifier(CSSM_DB_UNIQUE_RECORD_PTR uniqueRecord, CSSM_DATA &recordID)
532 {
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);
537
538 // copy the data
539 uint32* dest = (uint32*) recordID.Data;
540 uint32* src = (uint32*) uniqueRecord->RecordIdentifier.Data;
541
542 dest[0] = htonl (src[0]);
543 dest[1] = htonl (src[1]);
544 dest[2] = htonl (src[2]);
545 dest[3] = 0;
546 }
547
548 void SSDatabaseImpl::ssCopyBlob(CSSM_DATA& data)
549 {
550 // get the blob from the database
551 CssmDataContainer dbb(allocator());
552 getDbBlobId(&dbb);
553
554 // copy the data back
555 data.Data = dbb.Data;
556 data.Length = dbb.Length;
557
558 // zap the return structure so that we don't get zapped when dbb goes out of scope...
559 dbb.Data = NULL;
560 dbb.Length = 0;
561 }
562
563 uint32
564 SSDatabaseImpl::dbBlobVersion() {
565 CssmDataContainer dbb(allocator());
566 getDbBlobId(&dbb);
567 return getDbVersionFromBlob(dbb);
568 }
569
570 uint32
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);
576 return version;
577 }
578
579 DbUniqueRecordImpl *
580 SSDatabaseImpl::newDbUniqueRecord()
581 {
582 return new SSUniqueRecordImpl(SSDatabase(this));
583 }
584
585 CssmClient::DbUniqueRecord
586 SSDatabaseImpl::getDbBlobId(CssmDataContainer *dbb)
587 {
588 CssmClient::DbUniqueRecord dbBlobId;
589
590 DbCursor cursor(SSDatabase(this));
591 cursor->recordType(DBBlobRelationID);
592 if (!cursor->next(NULL, dbb, dbBlobId))
593 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
594
595 return dbBlobId;
596 }
597
598
599
600 SSUniqueRecordImpl::SSUniqueRecordImpl(const SSDatabase &db)
601 : DbUniqueRecord::Impl(db)
602 {
603 }
604
605 SSUniqueRecordImpl::~SSUniqueRecordImpl()
606 {
607 }
608
609 SSDatabase
610 SSUniqueRecordImpl::database() const
611 {
612 return parent<SSDatabase>();
613 }