2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
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
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.
20 // database - database session management
22 #include "xdatabase.h"
23 #include "agentquery.h"
26 #include "cfnotifier.h" // legacy
27 #include "notifications.h"
28 #include "SecurityAgentClient.h"
29 #include <Security/acl_any.h> // for default owner ACLs
33 // The map of database common segments
35 Mutex
Database::commonLock
;
36 Database::CommonMap
Database::commons
;
40 // Create a Database object from initial parameters (create operation)
42 Database::Database(const DLDbIdentifier
&id
, const DBParameters
¶ms
, Process
&proc
,
43 const AccessCredentials
*cred
, const AclEntryPrototype
*owner
)
44 : SecurityServerAcl(dbAcl
, CssmAllocator::standard()), process(proc
),
45 mValidData(false), version(0), mBlob(NULL
)
47 // save a copy of the credentials for later access control
48 mCred
= DataWalkers::copy(cred
, CssmAllocator::standard());
50 // create a new random signature to complete the DLDbIdentifier
52 Server::active().random(newSig
.bytes
);
53 DbIdentifier
ident(id
, newSig
);
55 // create common block and initialize
56 common
= new Common(ident
);
57 StLock
<Mutex
> _(*common
);
58 { StLock
<Mutex
> _(commonLock
);
59 assert(commons
.find(ident
) == commons
.end()); // better be new!
60 commons
[ident
] = common
= new Common(ident
);
63 // new common is now visible but we hold its lock
65 // obtain initial passphrase and generate keys
66 common
->mParams
= params
;
67 common
->setupKeys(cred
);
69 // establish initial ACL
71 cssmSetInitial(*owner
);
73 cssmSetInitial(new AnyAclSubject());
76 // for now, create the blob immediately
77 //@@@ this could be deferred, at the cost of some additional
78 //@@@ state monitoring. What happens if it locks before we have a blob?
81 // register with process
82 process
.addDatabase(this);
84 IFDEBUG(debug("SSdb", "database %s(%p) created, common at %p",
85 common
->dbName(), this, common
));
86 IFDUMPING("SSdb", debugDump("creation complete"));
91 // Create a Database object from a database blob (decoding)
93 Database::Database(const DLDbIdentifier
&id
, const DbBlob
*blob
, Process
&proc
,
94 const AccessCredentials
*cred
)
95 : SecurityServerAcl(dbAcl
, CssmAllocator::standard()), process(proc
),
96 mValidData(false), version(0)
98 // perform basic validation on the incoming blob
100 blob
->validate(CSSMERR_APPLEDL_INVALID_DATABASE_BLOB
);
101 switch (blob
->version
) {
102 #if defined(COMPAT_OSX_10_0)
103 case blob
->version_MacOS_10_0
:
106 case blob
->version_MacOS_10_1
:
109 CssmError::throwMe(CSSMERR_APPLEDL_INCOMPATIBLE_DATABASE_BLOB
);
112 // save a copy of the credentials for later access control
113 mCred
= DataWalkers::copy(cred
, CssmAllocator::standard());
115 // check to see if we already know about this database
116 DbIdentifier
ident(id
, blob
->randomSignature
);
117 StLock
<Mutex
> mapLock(commonLock
);
118 CommonMap::iterator it
= commons
.find(ident
);
119 if (it
!= commons
.end()) {
121 common
= it
->second
; // reuse common component
122 //@@@ arbitrate sequence number here, perhaps update common->mParams
123 StLock
<Mutex
> _(*common
); // lock common against other users
125 IFDEBUG(debug("SSdb",
126 "open database %s(%p) version %lx at known common %p(%d)",
127 common
->dbName(), this, blob
->version
, common
, int(common
->useCount
)));
130 commons
[ident
] = common
= new Common(ident
);
131 common
->mParams
= blob
->params
;
133 IFDEBUG(debug("SSdb", "open database %s(%p) version %lx with new common %p",
134 common
->dbName(), this, blob
->version
, common
));
137 // register with process
138 process
.addDatabase(this);
140 mBlob
= blob
->copy();
141 IFDUMPING("SSdb", debugDump("end of decode"));
146 // Destroy a Database
148 Database::~Database()
150 IFDEBUG(debug("SSdb", "deleting database %s(%p) common %p (%d refs)",
151 common
->dbName(), this, common
, int(common
->useCount
)));
152 IFDUMPING("SSdb", debugDump("deleting database instance"));
153 process
.removeDatabase(this);
154 CssmAllocator::standard().free(mCred
);
156 // take the commonLock to avoid races against re-use of the common
157 StLock
<Mutex
> __(commonLock
);
158 if (--common
->useCount
== 0 && common
->isLocked()) {
159 // last use of this database, and it's locked - discard
160 IFDUMPING("SSdb", debugDump("discarding common"));
162 } else if (common
->useCount
== 0)
163 IFDUMPING("SSdb", debugDump("retained because it's unlocked"));
168 // (Re-)Authenticate the database. This changes the stored credentials.
170 void Database::authenticate(const AccessCredentials
*cred
)
172 StLock
<Mutex
> _(*common
);
173 CssmAllocator::standard().free(mCred
);
174 mCred
= DataWalkers::copy(cred
, CssmAllocator::standard());
179 // Encode the current database as a blob.
180 // Note that this returns memory we own and keep.
182 DbBlob
*Database::encode()
184 StLock
<Mutex
> _(*common
);
186 // unlock the database
189 // create new up-to-date blob
190 DbBlob
*blob
= common
->encode(*this);
191 CssmAllocator::standard().free(mBlob
);
193 version
= common
->version
;
194 debug("SSdb", "encoded database %p(%s) version %ld", this, dbName(), version
);
203 // Change the passphrase on a database
205 void Database::changePassphrase(const AccessCredentials
*cred
)
207 StLock
<Mutex
> _(*common
);
209 CssmAutoData
passphrase(CssmAllocator::standard(CssmAllocator::sensitive
));
210 if (getBatchPassphrase(cred
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
, passphrase
)) {
211 // incoming sample contained data for unlock
212 makeUnlocked(passphrase
);
214 // perform standard unlock
217 } else if (!mValidData
) // need to decode to get our ACLs, passphrase available
218 decode(common
->passphrase
);
220 // get the new passphrase
221 // @@@ unstaged version -- revise to filter passphrases
222 Process
&cltProc
= Server::active().connection().process
;
223 IFDEBUG(debug("SSdb", "New passphrase query from PID %d (UID %d)", cltProc
.pid(), cltProc
.uid()));
224 QueryNewPassphrase
query(cltProc
.uid(), cltProc
.session
, *common
, SecurityAgent::changePassphrase
);
225 query(cred
, common
->passphrase
);
226 common
->version
++; // blob state changed
227 IFDEBUG(debug("SSdb", "Database %s(%p) passphrase changed", common
->dbName(), this));
229 // send out a notification
230 KeychainNotifier::passphraseChanged(identifier());
231 notify(passphraseChangedEvent
);
233 // I guess this counts as an activity
239 // Unlock this database (if needed) by obtaining the passphrase in some
240 // suitable way and then proceeding to unlock with it. Performs retries
241 // where appropriate. Does absolutely nothing if the database is already unlocked.
243 void Database::unlock()
245 StLock
<Mutex
> _(*common
);
249 void Database::makeUnlocked()
251 IFDUMPING("SSdb", debugDump("default procedures unlock"));
253 assert(mBlob
|| (mValidData
&& common
->passphrase
));
255 Process
&cltProc
= Server::active().connection().process
;
256 IFDEBUG(debug("SSdb", "Unlock query from process %d (UID %d)", cltProc
.pid(), cltProc
.uid()));
257 QueryUnlock
query(cltProc
.uid(), cltProc
.session
, *this);
259 if (isLocked()) // still locked, unlock failed
260 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
262 // successfully unlocked
263 activity(); // set timeout timer
264 } else if (!mValidData
) // need to decode to get our ACLs, passphrase available
265 decode(common
->passphrase
);
270 // Perform programmatic unlock of a database, given a passphrase.
272 void Database::unlock(const CssmData
&passphrase
)
274 StLock
<Mutex
> _(*common
);
275 makeUnlocked(passphrase
);
278 void Database::makeUnlocked(const CssmData
&passphrase
)
281 if (decode(passphrase
))
284 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
285 } else if (!mValidData
)
286 decode(common
->passphrase
);
291 // Perform an actual unlock operation given a passphrase.
292 // Caller must hold common lock.
294 bool Database::decode(const CssmData
&passphrase
)
296 if (mValidData
&& common
->passphrase
) { // just check
297 return common
->unlock(passphrase
);
298 } else { // decode our blob
300 void *privateAclBlob
;
301 if (common
->unlock(mBlob
, passphrase
, &privateAclBlob
)) {
303 importBlob(mBlob
->publicAclBlob(), privateAclBlob
);
306 CssmAllocator::standard().free(privateAclBlob
);
315 // Verify a putative database passphrase.
316 // This requires that the database be already unlocked;
317 // it will not unlock the database (and will not lock it
318 // if the proffered phrase is wrong).
320 bool Database::validatePassphrase(const CssmData
&passphrase
) const
323 return passphrase
== common
->passphrase
;
328 // Lock this database
330 void Database::lock()
337 // Lock all databases we know of.
338 // This is an interim stop-gap measure, until we can work out how database
339 // state should interact with true multi-session operation.
341 void Database::lockAllDatabases(bool forSleep
)
343 StLock
<Mutex
> _(commonLock
); // hold all changes to Common map
344 debug("SSdb", "locking all %d known databases", int(commons
.size()));
345 for (CommonMap::iterator it
= commons
.begin(); it
!= commons
.end(); it
++)
346 it
->second
->lock(true, forSleep
); // lock, already holding commonLock
351 // Given a Key for this database, encode it into a blob and return it.
353 KeyBlob
*Database::encodeKey(const CssmKey
&key
, const CssmData
&pubAcl
, const CssmData
&privAcl
)
357 // tell the cryptocore to form the key blob
358 return common
->encodeKeyCore(key
, pubAcl
, privAcl
);
363 // Given a "blobbed" key for this database, decode it into its real
364 // key object and (re)populate its ACL.
366 void Database::decodeKey(KeyBlob
*blob
, CssmKey
&key
,
367 void * &pubAcl
, void * &privAcl
)
369 makeUnlocked(); // we need our keys
371 common
->decodeKeyCore(blob
, key
, pubAcl
, privAcl
);
372 // memory protocol: pubAcl points into blob; privAcl was allocated
379 // Modify database parameters
381 void Database::setParameters(const DBParameters
¶ms
)
383 StLock
<Mutex
> _(*common
);
385 common
->mParams
= params
;
386 common
->version
++; // invalidate old blobs
392 // Retrieve database parameters
394 void Database::getParameters(DBParameters
¶ms
)
396 StLock
<Mutex
> _(*common
);
398 params
= common
->mParams
;
399 //activity(); // getting parameters does not reset the idle timer
404 // Intercept ACL change requests and reset blob validity
406 void Database::instantiateAcl()
408 StLock
<Mutex
> _(*common
);
412 void Database::noticeAclChange()
414 StLock
<Mutex
> _(*common
);
418 const Database
*Database::relatedDatabase() const
425 #if defined(DEBUGDUMP)
427 void Database::debugDump(const char *msg
)
430 const Signature
&sig
= common
->identifier();
431 uint32 sig4
; memcpy(&sig4
, sig
.bytes
, sizeof(sig4
));
432 Debug::dump("** %s(%8.8lx) common=%p(%ld) %s\n",
433 common
->dbName(), sig4
, common
, common
->useCount
, msg
);
435 Debug::dump(" locked");
437 Time::Absolute when
= common
->when();
438 time_t whenTime
= time_t(when
);
439 Debug::dump(" UNLOCKED(%24.24s/%.2g)", ctime(&whenTime
),
440 (when
- Time::now()).seconds());
442 Debug::dump(" %s blobversion=%ld/%ld %svalidData",
443 (common
->isValid() ? "validkeys" : "!validkeys"),
444 version
, common
->version
,
445 (mValidData
? "" : "!"));
446 Debug::dump(" Params=(%ld %d)\n",
447 common
->mParams
.idleTimeout
, common
->mParams
.lockOnSleep
);
454 // Database::Common basic features
456 Database::Common::Common(const DbIdentifier
&id
)
457 : mIdentifier(id
), sequence(0), passphrase(CssmAllocator::standard(CssmAllocator::sensitive
)),
458 useCount(0), version(1),
462 Database::Common::~Common()
464 // explicitly unschedule ourselves
465 Server::active().clearTimer(this);
469 void Database::discard(Common
*common
)
471 // LOCKING: commonLock held, *common NOT held
472 debug("SSdb", "discarding dbcommon %p (no users, locked)", common
);
473 commons
.erase(common
->identifier());
477 bool Database::Common::unlock(DbBlob
*blob
, const CssmData
&passphrase
,
478 void **privateAclBlob
)
481 // Tell the cryptocore to (try to) decode itself. This will fail
482 // in an astonishing variety of ways if the passphrase is wrong.
483 decodeCore(blob
, passphrase
, privateAclBlob
);
485 //@@@ which errors should we let through? Any?
489 // save the passphrase (we'll need it for database encoding)
490 this->passphrase
= passphrase
;
492 // retrieve some public arguments
493 mParams
= blob
->params
;
495 // now successfully unlocked
501 // broadcast unlock notification
502 KeychainNotifier::unlock(identifier());
503 notify(unlockedEvent
);
509 // Fast-path unlock: secrets already valid; just check passphrase and approve.
511 bool Database::Common::unlock(const CssmData
&passphrase
)
515 if (passphrase
== this->passphrase
) {
517 KeychainNotifier::unlock(identifier());
518 notify(unlockedEvent
);
521 return false; // failed
523 return true; // was unlocked; no problem
526 void Database::Common::lock(bool holdingCommonLock
, bool forSleep
)
528 StLock
<Mutex
> locker(*this);
530 if (forSleep
&& !mParams
.lockOnSleep
)
531 return; // it doesn't want to
533 //@@@ discard secrets here? That would make fast-path impossible.
535 KeychainNotifier::lock(identifier());
538 // if no database refers to us now, we're history
539 StLock
<Mutex
> _(commonLock
, false);
540 if (!holdingCommonLock
)
543 locker
.unlock(); // release object lock
549 DbBlob
*Database::Common::encode(Database
&db
)
551 assert(!isLocked()); // must have been unlocked by caller
553 // export database ACL to blob form
554 CssmData pubAcl
, privAcl
;
555 db
.exportBlob(pubAcl
, privAcl
);
557 // tell the cryptocore to form the blob
559 form
.randomSignature
= identifier();
560 form
.sequence
= sequence
;
561 form
.params
= mParams
;
562 DbBlob
*blob
= encodeCore(form
, passphrase
, pubAcl
, privAcl
);
565 db
.allocator
.free(pubAcl
);
566 db
.allocator
.free(privAcl
);
572 // Send out database-related notifications
574 void Database::Common::notify(Listener::Event event
)
576 IFDEBUG(debug("SSdb", "common %s(%p) sending event %ld", dbName(), this, event
));
577 DLDbFlatIdentifier
flatId(mIdentifier
); // walkable form of DLDbIdentifier
578 CssmAutoData
data(CssmAllocator::standard());
579 copy(&flatId
, CssmAllocator::standard(), data
.get());
580 Listener::notify(Listener::databaseNotifications
, event
, data
);
585 // Initialize a (new) database's key information.
586 // This acquires the passphrase in the appropriate way.
587 // When (successfully) done, the database is in the unlocked state.
589 void Database::Common::setupKeys(const AccessCredentials
*cred
)
591 // get the new passphrase
592 // @@@ Un-staged version of the API - revise with acceptability tests
593 Process
&cltProc
= Server::active().connection().process
;
594 IFDEBUG(debug("SSdb", "New passphrase request from process %d (UID %d)", cltProc
.pid(), cltProc
.uid()));
595 QueryNewPassphrase
query(cltProc
.uid(), cltProc
.session
, *this, SecurityAgent::newDatabase
);
596 query(cred
, passphrase
);
598 // we have the passphrase now
599 generateNewSecrets();
601 // we're unlocked now
608 // Perform deferred lock processing for a database.
610 void Database::Common::action()
612 IFDEBUG(debug("SSdb", "common %s(%p) locked by timer (%d refs)",
613 dbName(), this, int(useCount
)));
617 void Database::Common::activity()
620 Server::active().setTimer(this, int(mParams
.idleTimeout
));