]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_cspdl/lib/SSDatabase.cpp
Security-57740.31.2.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::ssInsert(CSSM_DB_RECORDTYPE recordType,
53 const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes,
54 const CSSM_DATA *data)
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::ssCreate(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 secnotice("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::ssCreateWithBlob(const DLDbIdentifier &dlDbIdentifier, const CSSM_DATA &blob)
289 {
290 try
291 {
292 bool autoCommit;
293 commonCreate(dlDbIdentifier, autoCommit);
294 secnotice("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::ssOpen(const DLDbIdentifier &dlDbIdentifier)
312 {
313 load(dlDbIdentifier);
314
315 CssmDataContainer dbb(allocator());
316 getDbBlobId(&dbb);
317
318 // Pull our version out of the database blob
319 mSSDbHandle = mClientSession.decodeDb(dlDbIdentifier, AccessCredentials::overlay(accessCredentials()), dbb);
320 }
321
322 void
323 SSDatabaseImpl::load(const DLDbIdentifier &dlDbIdentifier) {
324 mIdentifier = dlDbIdentifier;
325 Db::Impl::open();
326
327 CssmDataContainer dbb(allocator());
328 getDbBlobId(&dbb);
329
330 secnotice("integrity", "loading %s", name());
331 }
332
333 void
334 SSDatabaseImpl::ssRecode(const CssmData &dbHandleArray, const CssmData &agentData)
335 {
336 // Start a transaction (Implies activate()).
337 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 0);
338
339 try
340 {
341 CssmDataContainer dbb(allocator());
342 // Make sure mSSDbHandle is valid.
343 CssmClient::DbUniqueRecord dbBlobId = getDbBlobId(&dbb);
344 if (mForked()) {
345 // re-establish the dbHandle with the SecurityServer
346 mSSDbHandle = mClientSession.decodeDb(mIdentifier,
347 AccessCredentials::overlay(accessCredentials()), dbb);
348 }
349 dbb.clear();
350
351 DbHandle successfulHdl = mClientSession.authenticateDbsForSync(dbHandleArray, agentData);
352
353 // Create a newDbHandle using the master secrets from the dbBlob we are
354 // recoding to.
355 SecurityServer::DbHandle clonedDbHandle =
356 mClientSession.recodeDbForSync(successfulHdl, mSSDbHandle);
357
358 // @@@ If the dbb changed since we fetched it we should abort or
359 // retry the operation here.
360
361 recodeHelper(clonedDbHandle, dbBlobId);
362
363 // Commit the transaction to the db
364 passThrough(CSSM_APPLEFILEDL_COMMIT, NULL);
365 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
366 reinterpret_cast<const void *>(1));
367 }
368 catch (...)
369 {
370 // Something went wrong rollback the transaction
371 passThrough(CSSM_APPLEFILEDL_ROLLBACK, NULL);
372 passThrough(CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
373 reinterpret_cast<const void *>(1));
374 throw;
375 }
376 }
377
378 uint32
379 SSDatabaseImpl::recodeDbToVersion(uint32 newBlobVersion) {
380 // Start a transaction (Implies activate()).
381 DLTransaction transaction(handle());
382
383 try
384 {
385 if(isLocked()) {
386 secnotice("integrity", "is currently locked");
387 } else {
388 secnotice("integrity", "is already unlocked");
389 }
390
391 CssmDataContainer dbb(allocator());
392 // Make sure mSSDbHandle is valid.
393 CssmClient::DbUniqueRecord dbBlobId = getDbBlobId(&dbb);
394
395 // always establish the dbHandle with the SecurityServer
396 mSSDbHandle = mClientSession.decodeDb(mIdentifier, AccessCredentials::overlay(accessCredentials()), dbb);
397 dbb.clear();
398
399 // Create a newDbHandle using the master secrets from the dbBlob we are recoding to.
400 secnotice("integrity", "recoding db with handle %d", mSSDbHandle);
401 SecurityServer::DbHandle clonedDbHandle = mClientSession.recodeDbToVersion(newBlobVersion, mSSDbHandle);
402 secnotice("integrity", "received db with handle %d", clonedDbHandle);
403
404 // @@@ If the dbb changed since we fetched it we should abort or
405 // retry the operation here.
406
407 uint32 newBlobVersion = recodeHelper(clonedDbHandle, dbBlobId);
408 secnotice("integrity", "committing transaction %d", clonedDbHandle);
409
410 // Commit the transaction to the db
411 transaction.commit();
412 return newBlobVersion;
413 }
414 catch (...)
415 {
416 throw;
417 }
418 }
419
420 void
421 SSDatabaseImpl::recodeFinished() {
422 mClientSession.recodeFinished(mSSDbHandle);
423 }
424
425 void SSDatabaseImpl::takeFileLock() {
426 if(mTransaction) {
427 // you're already in the middle of a file lock.
428 return;
429 }
430 mTransaction = new DLTransaction(handle());
431 passThrough(CSSM_APPLEFILEDL_TAKE_FILE_LOCK, NULL, NULL);
432 }
433
434 void SSDatabaseImpl::releaseFileLock(bool success) {
435 if(mTransaction) {
436 try {
437 if(success) {
438 mTransaction->commit();
439 }
440 // If we didn't commit, the destructor will roll back and re-enable autocommit
441 delete mTransaction;
442 mTransaction = NULL;
443 } catch(...) {
444 mTransaction = NULL;
445 throw;
446 }
447 }
448 }
449
450 void SSDatabaseImpl::makeBackup() {
451 passThrough(CSSM_APPLEFILEDL_MAKE_BACKUP, NULL, NULL);
452 }
453
454 void SSDatabaseImpl::makeCopy(const char* path) {
455 passThrough(CSSM_APPLEFILEDL_MAKE_COPY, path, NULL);
456 }
457
458 void SSDatabaseImpl::deleteFile() {
459 passThrough(CSSM_APPLEFILEDL_DELETE_FILE, NULL, NULL);
460 }
461
462 SSDatabase SSDatabaseImpl::ssCloneTo(const DLDbIdentifier& dldbidentifier) {
463 makeCopy(dldbidentifier.dbName());
464 SSDatabase db(mClientSession, dl(), dldbidentifier.dbName(), dldbidentifier.dbLocation());
465
466 db->load(dldbidentifier);
467 db->mSSDbHandle = mClientSession.cloneDb(dldbidentifier, mSSDbHandle);
468
469 return db;
470 }
471
472
473
474 uint32 SSDatabaseImpl::recodeHelper(SecurityServer::DbHandle clonedDbHandle, CssmClient::DbUniqueRecord& dbBlobId) {
475 // Recode all keys
476 DbCursor cursor(SSDatabase(this));
477 cursor->recordType(CSSM_DL_DB_RECORD_ALL_KEYS);
478 CssmDataContainer keyBlob(allocator());
479 CssmClient::DbUniqueRecord keyBlobId;
480 DbAttributes attributes;
481 while (cursor->next(&attributes, &keyBlob, keyBlobId))
482 {
483 KeyHandle keyHandle = 0;
484 try {
485 // Decode the old key
486 CssmKey::Header header;
487 keyHandle = mClientSession.decodeKey(mSSDbHandle, keyBlob, header);
488 // Recode the key
489 CssmDataContainer newKeyBlob(mClientSession.returnAllocator);
490 mClientSession.recodeKey(mSSDbHandle, keyHandle, clonedDbHandle, newKeyBlob);
491 mClientSession.releaseKey(keyHandle);
492 keyHandle = 0;
493 // Write the recoded key blob to the database
494 keyBlobId->modify(attributes.recordType(), NULL, &newKeyBlob,
495 CSSM_DB_MODIFY_ATTRIBUTE_NONE);
496 } catch (CssmError cssme) {
497 const char* errStr = cssmErrorString(cssme.error);
498 secnotice("integrity", "corrupt item while recoding: %d %s", (int) cssme.error, errStr);
499 secnotice("integrity", "deleting corrupt item");
500
501
502 keyBlobId->deleteRecord();
503
504 if(keyHandle != 0) {
505 // tell securityd not to worry about this key again
506 try {
507 secnotice("integrity", "releasing corrupt key");
508 mClientSession.releaseKey(keyHandle);
509 } catch(CssmError cssme) {
510 // swallow the error
511 const char* errStr = cssmErrorString(cssme.error);
512 secnotice("integrity", "couldn't release corrupt key: %d %s", (int) cssme.error, errStr);
513 }
514 }
515 }
516 }
517
518 // Commit the new blob to securityd, reencode the db blob, release the
519 // cloned db handle and commit the new blob to the db.
520 CssmDataContainer dbb(allocator());
521 secnotice("integrity", "committing %d", clonedDbHandle);
522 mClientSession.commitDbForSync(mSSDbHandle, clonedDbHandle,
523 dbb, allocator());
524 dbBlobId->modify(DBBlobRelationID, NULL, &dbb,
525 CSSM_DB_MODIFY_ATTRIBUTE_NONE);
526 return getDbVersionFromBlob(dbb);
527 }
528
529
530 void SSDatabaseImpl::getRecordIdentifier(CSSM_DB_UNIQUE_RECORD_PTR uniqueRecord, CSSM_DATA &recordID)
531 {
532 // the unique ID is composed of three uint32s (plus one filler word). Pull
533 // them out and byte swap them
534 recordID.Length = sizeof (uint32) * kNumIDWords;
535 recordID.Data = (uint8*) allocator().malloc(recordID.Length);
536
537 // copy the data
538 uint32* dest = (uint32*) recordID.Data;
539 uint32* src = (uint32*) uniqueRecord->RecordIdentifier.Data;
540
541 dest[0] = htonl (src[0]);
542 dest[1] = htonl (src[1]);
543 dest[2] = htonl (src[2]);
544 dest[3] = 0;
545 }
546
547 void SSDatabaseImpl::ssCopyBlob(CSSM_DATA& data)
548 {
549 // get the blob from the database
550 CssmDataContainer dbb(allocator());
551 getDbBlobId(&dbb);
552
553 // copy the data back
554 data.Data = dbb.Data;
555 data.Length = dbb.Length;
556
557 // zap the return structure so that we don't get zapped when dbb goes out of scope...
558 dbb.Data = NULL;
559 dbb.Length = 0;
560 }
561
562 uint32
563 SSDatabaseImpl::dbBlobVersion() {
564 CssmDataContainer dbb(allocator());
565 getDbBlobId(&dbb);
566 return getDbVersionFromBlob(dbb);
567 }
568
569 uint32
570 SSDatabaseImpl::getDbVersionFromBlob(const CssmData& dbb) {
571 DbBlob* x = Allocator::standard().malloc<DbBlob>(dbb.length());
572 memcpy(x, dbb, dbb.length());
573 uint32 version = x->version();
574 Allocator::standard().free(x);
575 return version;
576 }
577
578 DbUniqueRecordImpl *
579 SSDatabaseImpl::newDbUniqueRecord()
580 {
581 return new SSUniqueRecordImpl(SSDatabase(this));
582 }
583
584 CssmClient::DbUniqueRecord
585 SSDatabaseImpl::getDbBlobId(CssmDataContainer *dbb)
586 {
587 CssmClient::DbUniqueRecord dbBlobId;
588
589 DbCursor cursor(SSDatabase(this));
590 cursor->recordType(DBBlobRelationID);
591 if (!cursor->next(NULL, dbb, dbBlobId))
592 CssmError::throwMe(CSSMERR_DL_DATABASE_CORRUPT);
593
594 return dbBlobId;
595 }
596
597
598
599 SSUniqueRecordImpl::SSUniqueRecordImpl(const SSDatabase &db)
600 : DbUniqueRecord::Impl(db)
601 {
602 }
603
604 SSUniqueRecordImpl::~SSUniqueRecordImpl()
605 {
606 }
607
608 SSDatabase
609 SSUniqueRecordImpl::database() const
610 {
611 return parent<SSDatabase>();
612 }