]> git.saurik.com Git - apple/security.git/blob - SecurityServer/xdatabase.cpp
Security-179.tar.gz
[apple/security.git] / SecurityServer / xdatabase.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, 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 // database - database session management
21 //
22 #include "xdatabase.h"
23 #include "agentquery.h"
24 #include "key.h"
25 #include "server.h"
26 #include "session.h"
27 #include "cfnotifier.h" // legacy
28 #include "notifications.h"
29 #include "SecurityAgentClient.h"
30 #include <Security/acl_any.h> // for default owner ACLs
31 #include <Security/wrapkey.h>
32 #include <Security/endian.h>
33
34
35 //
36 // Create a Database object from initial parameters (create operation)
37 //
38 Database::Database(const DLDbIdentifier &id, const DBParameters &params, Process &proc,
39 const AccessCredentials *cred, const AclEntryPrototype *owner)
40 : SecurityServerAcl(dbAcl, CssmAllocator::standard()), process(proc),
41 mValidData(false), version(0), mBlob(NULL)
42 {
43 // save a copy of the credentials for later access control
44 mCred = DataWalkers::copy(cred, CssmAllocator::standard());
45
46 // create a new random signature to complete the DLDbIdentifier
47 Signature newSig;
48 Server::active().random(newSig.bytes);
49 DbIdentifier ident(id, newSig);
50
51 // create common block and initialize
52 CommonMap &commons = proc.session.databases();
53 common = new Common(ident, commons);
54 StLock<Mutex> _(*common);
55 { StLock<Mutex> _(commons);
56 assert(commons.find(ident) == commons.end()); // better be new!
57 commons[ident] = common;
58 common->useCount++;
59 }
60 // new common is now visible but we hold its lock
61
62 // establish the new master secret
63 establishNewSecrets(cred, SecurityAgent::newDatabase);
64
65 // set initial database parameters
66 common->mParams = params;
67
68 // we're unlocked now
69 common->makeNewSecrets();
70
71 // establish initial ACL
72 if (owner)
73 cssmSetInitial(*owner);
74 else
75 cssmSetInitial(new AnyAclSubject());
76 mValidData = true;
77
78 // for now, create the blob immediately
79 encode();
80
81 // register with process
82 process.addDatabase(this);
83
84 secdebug("SSdb", "database %s(%p) created, common at %p",
85 common->dbName(), this, common);
86 IFDUMPING("SSdb", debugDump("creation complete"));
87 }
88
89
90 //
91 // Create a Database object from a database blob (decoding)
92 //
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)
97 {
98 // perform basic validation on the incoming blob
99 assert(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:
104 break;
105 #endif
106 case blob->version_MacOS_10_1:
107 break;
108 default:
109 CssmError::throwMe(CSSMERR_APPLEDL_INCOMPATIBLE_DATABASE_BLOB);
110 }
111
112 // save a copy of the credentials for later access control
113 mCred = DataWalkers::copy(cred, CssmAllocator::standard());
114
115 // check to see if we already know about this database
116 DbIdentifier ident(id, blob->randomSignature);
117 CommonMap &commons = proc.session.databases();
118 StLock<Mutex> mapLock(commons);
119 CommonMap::iterator it = commons.find(ident);
120 if (it != commons.end()) {
121 // already there
122 common = it->second; // reuse common component
123 //@@@ arbitrate sequence number here, perhaps update common->mParams
124 StLock<Mutex> _(*common); // lock common against other users
125 common->useCount++;
126 secdebug("SSdb",
127 "open database %s(%p) version %lx at known common %p(%d)",
128 common->dbName(), this, blob->version(), common, int(common->useCount));
129 } else {
130 // newly introduced
131 commons[ident] = common = new Common(ident, commons);
132 common->mParams = blob->params;
133 common->useCount++;
134 secdebug("SSdb", "open database %s(%p) version %lx with new common %p",
135 common->dbName(), this, blob->version(), common);
136 }
137
138 // register with process
139 process.addDatabase(this);
140
141 mBlob = blob->copy();
142 IFDUMPING("SSdb", debugDump("end of decode"));
143 }
144
145
146 //
147 // Destroy a Database
148 //
149 Database::~Database()
150 {
151 secdebug("SSdb", "deleting database %s(%p) common %p (%d refs)",
152 common->dbName(), this, common, int(common->useCount));
153 IFDUMPING("SSdb", debugDump("deleting database instance"));
154 process.removeDatabase(this);
155 CssmAllocator::standard().free(mCred);
156 CssmAllocator::standard().free(mBlob);
157
158 // take the commonLock to avoid races against re-use of the common
159 CommonMap &commons = process.session.databases();
160 StLock<Mutex> __(commons);
161 if (--common->useCount == 0 && common->isLocked()) {
162 // last use of this database, and it's locked - discard
163 IFDUMPING("SSdb", debugDump("discarding common"));
164 discard(common);
165 } else if (common->useCount == 0)
166 IFDUMPING("SSdb", debugDump("retained because it's unlocked"));
167 }
168
169
170 //
171 // (Re-)Authenticate the database. This changes the stored credentials.
172 //
173 void Database::authenticate(const AccessCredentials *cred)
174 {
175 StLock<Mutex> _(*common);
176 AccessCredentials *newCred = DataWalkers::copy(cred, CssmAllocator::standard());
177 CssmAllocator::standard().free(mCred);
178 mCred = newCred;
179 }
180
181
182 //
183 // Return the database blob, recalculating it as needed.
184 //
185 DbBlob *Database::blob()
186 {
187 StLock<Mutex> _(*common);
188 if (!validBlob()) {
189 makeUnlocked(); // unlock to get master secret
190 encode(); // (re)encode blob if needed
191 }
192 activity(); // reset timeout
193 assert(validBlob()); // better have a valid blob now...
194 return mBlob;
195 }
196
197
198 //
199 // Encode the current database as a blob.
200 // Note that this returns memory we own and keep.
201 // Caller must hold common lock.
202 //
203 void Database::encode()
204 {
205 DbBlob *blob = common->encode(*this);
206 CssmAllocator::standard().free(mBlob);
207 mBlob = blob;
208 version = common->version;
209 secdebug("SSdb", "encoded database %p common %p(%s) version %ld params=(%ld,%d)",
210 this, common, dbName(), version,
211 common->mParams.idleTimeout, common->mParams.lockOnSleep);
212 }
213
214
215 //
216 // Change the passphrase on a database
217 //
218 void Database::changePassphrase(const AccessCredentials *cred)
219 {
220 // get and hold the common lock (don't let other threads break in here)
221 StLock<Mutex> _(*common);
222
223 // establish OLD secret - i.e. unlock the database
224 //@@@ do we want to leave the final lock state alone?
225 makeUnlocked(cred);
226
227 // establish NEW secret
228 establishNewSecrets(cred, SecurityAgent::changePassphrase);
229 common->version++; // blob state changed
230 secdebug("SSdb", "Database %s(%p) master secret changed", common->dbName(), this);
231 encode(); // force rebuild of local blob
232
233 // send out a notification
234 KeychainNotifier::passphraseChanged(identifier());
235
236 // I guess this counts as an activity
237 activity();
238 }
239
240
241 //
242 // Extract the database master key as a proper Key object.
243 //
244 Key *Database::extractMasterKey(Database *db,
245 const AccessCredentials *cred, const AclEntryPrototype *owner,
246 uint32 usage, uint32 attrs)
247 {
248 // get and hold common lock
249 StLock<Mutex> _(*common);
250
251 // unlock to establish master secret
252 makeUnlocked(cred);
253
254 // extract the raw cryptographic key
255 CssmClient::WrapKey wrap(Server::csp(), CSSM_ALGID_NONE);
256 CssmKey key;
257 wrap(common->masterKey(), key);
258
259 // make the key object and return it
260 return new Key(db, key, attrs & Key::managedAttributes, owner);
261 }
262
263
264 //
265 // Construct a binary blob of moderate size that is suitable for constructing
266 // an index identifying this database.
267 // We construct this from the database's marker blob, which is created with
268 // the database is made, and stays stable thereafter.
269 // Note: Ownership of the index blob passes to the caller.
270 // @@@ This means that physical copies share this index.
271 //
272 void Database::getDbIndex(CssmData &indexData)
273 {
274 if (!mBlob)
275 encode(); // force blob creation
276 assert(mBlob);
277 CssmData signature = CssmData::wrap(mBlob->randomSignature);
278 indexData = CssmAutoData(CssmAllocator::standard(), signature).release();
279 }
280
281
282 //
283 // Unlock this database (if needed) by obtaining the master secret in some
284 // suitable way and then proceeding to unlock with it.
285 // Does absolutely nothing if the database is already unlocked.
286 // The makeUnlocked forms are identical except the assume the caller already
287 // holds the common lock.
288 //
289 void Database::unlock()
290 {
291 StLock<Mutex> _(*common);
292 makeUnlocked();
293 }
294
295 void Database::makeUnlocked()
296 {
297 return makeUnlocked(mCred);
298 }
299
300 void Database::makeUnlocked(const AccessCredentials *cred)
301 {
302 IFDUMPING("SSdb", debugDump("default procedures unlock"));
303 if (isLocked()) {
304 assert(mBlob || (mValidData && common->hasMaster()));
305 establishOldSecrets(cred);
306 activity(); // set timeout timer
307 } else if (!mValidData) { // need to decode to get our ACLs, passphrase available
308 if (!decode())
309 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
310 }
311 assert(!isLocked());
312 assert(mValidData);
313 }
314
315
316 //
317 // The following unlock given an explicit passphrase, rather than using
318 // (special cred sample based) default procedures.
319 //
320 void Database::unlock(const CssmData &passphrase)
321 {
322 StLock<Mutex> _(*common);
323 makeUnlocked(passphrase);
324 }
325
326 void Database::makeUnlocked(const CssmData &passphrase)
327 {
328 if (isLocked()) {
329 if (decode(passphrase))
330 return;
331 else
332 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
333 } else if (!mValidData) { // need to decode to get our ACLs, passphrase available
334 if (!decode())
335 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
336 }
337 assert(!isLocked());
338 assert(mValidData);
339 }
340
341
342 //
343 // Nonthrowing passphrase-based unlock. This returns false if unlock failed.
344 // Note that this requires an explicitly given passphrase.
345 // Caller must hold common lock.
346 //
347 bool Database::decode(const CssmData &passphrase)
348 {
349 assert(mBlob);
350 common->setup(mBlob, passphrase);
351 return decode();
352 }
353
354
355 //
356 // Given the established master secret, decode the working keys and other
357 // functional secrets for this database. Return false (do NOT throw) if
358 // the decode fails. Call this in low(er) level code once you established
359 // the master key.
360 //
361 bool Database::decode()
362 {
363 assert(mBlob);
364 assert(common->hasMaster());
365 void *privateAclBlob;
366 if (common->unlock(mBlob, &privateAclBlob)) {
367 if (!mValidData) {
368 importBlob(mBlob->publicAclBlob(), privateAclBlob);
369 mValidData = true;
370 }
371 CssmAllocator::standard().free(privateAclBlob);
372 return true;
373 }
374 secdebug("SSdb", "%p decode failed", this);
375 return false;
376 }
377
378
379 //
380 // Given an AccessCredentials for this database, wring out the existing primary
381 // database secret by whatever means necessary.
382 // On entry, caller must hold the database common lock. It will be held throughout.
383 // On exit, the crypto core has its master secret. If things go wrong,
384 // we will throw a suitable exception. Note that encountering any malformed
385 // credential sample will throw, but this is not guaranteed -- don't assume
386 // that NOT throwing means creds is entirely well-formed.
387 //
388 // How this works:
389 // Walk through the creds. Fish out those credentials (in order) that
390 // are for unlock processing (they have no ACL subject correspondents),
391 // and (try to) obey each in turn, until one produces a valid secret
392 // or you run out. If no special samples are found at all, interpret that as
393 // "use the system global default," which happens to be hard-coded right here.
394 //
395 void Database::establishOldSecrets(const AccessCredentials *creds)
396 {
397 list<CssmSample> samples;
398 if (creds && creds->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, samples)) {
399 for (list<CssmSample>::iterator it = samples.begin(); it != samples.end(); it++) {
400 TypedList &sample = *it;
401 sample.checkProper();
402 switch (sample.type()) {
403 // interactively prompt the user - no additional data
404 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT:
405 {
406 secdebug("SSdb", "%p attempting interactive unlock", this);
407 QueryUnlock query(*this);
408 if (query() == SecurityAgent::noReason)
409 return;
410 }
411 break;
412 // try to use an explicitly given passphrase - Data:passphrase
413 case CSSM_SAMPLE_TYPE_PASSWORD:
414 if (sample.length() != 2)
415 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
416 secdebug("SSdb", "%p attempting passphrase unlock", this);
417 if (decode(sample[1]))
418 return;
419 break;
420 // try to open with a given master key - Data:CSP or KeyHandle, Data:CssmKey
421 case CSSM_WORDID_SYMMETRIC_KEY:
422 assert(mBlob);
423 secdebug("SSdb", "%p attempting explicit key unlock", this);
424 common->setup(mBlob, keyFromCreds(sample));
425 if (decode())
426 return;
427 break;
428 // explicitly defeat the default action but don't try anything in particular
429 case CSSM_WORDID_CANCELED:
430 secdebug("SSdb", "%p defeat default action", this);
431 break;
432 default:
433 // Unknown sub-sample for unlocking.
434 // If we wanted to be fascist, we could now do
435 // CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
436 // But instead we try to be tolerant and continue on.
437 // This DOES however count as an explicit attempt at specifying unlock,
438 // so we will no longer try the default case below...
439 secdebug("SSdb", "%p unknown sub-sample unlock (%ld) ignored", this, sample.type());
440 break;
441 }
442 }
443 } else {
444 // default action
445 assert(mBlob);
446 SystemKeychainKey systemKeychain(kSystemUnlockFile);
447 if (systemKeychain.matches(mBlob->randomSignature)) {
448 secdebug("SSdb", "%p attempting system unlock", this);
449 common->setup(mBlob, CssmClient::Key(Server::csp(), systemKeychain.key(), true));
450 if (decode())
451 return;
452 }
453
454 QueryUnlock query(*this);
455 if (query() == SecurityAgent::noReason)
456 return;
457 }
458
459 // out of options - no secret obtained
460 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
461 }
462
463
464 //
465 // Same thing, but obtain a new secret somehow and set it into the common.
466 //
467 void Database::establishNewSecrets(const AccessCredentials *creds, SecurityAgent::Reason reason)
468 {
469 list<CssmSample> samples;
470 if (creds && creds->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK, samples)) {
471 for (list<CssmSample>::iterator it = samples.begin(); it != samples.end(); it++) {
472 TypedList &sample = *it;
473 sample.checkProper();
474 switch (sample.type()) {
475 // interactively prompt the user
476 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT:
477 {
478 secdebug("SSdb", "%p specified interactive passphrase", this);
479 QueryNewPassphrase query(*this, reason);
480 CssmAutoData passphrase(CssmAllocator::standard(CssmAllocator::sensitive));
481 if (query(passphrase) == SecurityAgent::noReason) {
482 common->setup(NULL, passphrase);
483 return;
484 }
485 }
486 break;
487 // try to use an explicitly given passphrase
488 case CSSM_SAMPLE_TYPE_PASSWORD:
489 secdebug("SSdb", "%p specified explicit passphrase", this);
490 if (sample.length() != 2)
491 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
492 common->setup(NULL, sample[1]);
493 return;
494 // try to open with a given master key
495 case CSSM_WORDID_SYMMETRIC_KEY:
496 secdebug("SSdb", "%p specified explicit master key", this);
497 common->setup(NULL, keyFromCreds(sample));
498 return;
499 // explicitly defeat the default action but don't try anything in particular
500 case CSSM_WORDID_CANCELED:
501 secdebug("SSdb", "%p defeat default action", this);
502 break;
503 default:
504 // Unknown sub-sample for acquiring new secret.
505 // If we wanted to be fascist, we could now do
506 // CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
507 // But instead we try to be tolerant and continue on.
508 // This DOES however count as an explicit attempt at specifying unlock,
509 // so we will no longer try the default case below...
510 secdebug("SSdb", "%p unknown sub-sample acquisition (%ld) ignored",
511 this, sample.type());
512 break;
513 }
514 }
515 } else {
516 // default action -- interactive (only)
517 QueryNewPassphrase query(*this, reason);
518 CssmAutoData passphrase(CssmAllocator::standard(CssmAllocator::sensitive));
519 if (query(passphrase) == SecurityAgent::noReason) {
520 common->setup(NULL, passphrase);
521 return;
522 }
523 }
524
525 // out of options - no secret obtained
526 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
527 }
528
529
530 //
531 // Given a (truncated) Database credentials TypedList specifying a master key,
532 // locate the key and return a reference to it.
533 //
534 CssmClient::Key Database::keyFromCreds(const TypedList &sample)
535 {
536 // decode TypedList structure (sample type; Data:CSPHandle; Data:CSSM_KEY)
537 assert(sample.type() == CSSM_WORDID_SYMMETRIC_KEY);
538 if (sample.length() != 3
539 || sample[1].type() != CSSM_LIST_ELEMENT_DATUM
540 || sample[2].type() != CSSM_LIST_ELEMENT_DATUM)
541 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
542 CSSM_CSP_HANDLE &handle = *sample[1].data().interpretedAs<CSSM_CSP_HANDLE>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
543 CssmKey &key = *sample[2].data().interpretedAs<CssmKey>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
544
545 if (key.header().cspGuid() == gGuidAppleCSPDL) {
546 // handleOrKey is a SecurityServer KeyHandle; ignore key argument
547 return Server::key(handle);
548 } else {
549 // not a KeyHandle reference; use key as a raw key
550 if (key.header().blobType() != CSSM_KEYBLOB_RAW)
551 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE);
552 if (key.header().keyClass() != CSSM_KEYCLASS_SESSION_KEY)
553 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
554 return CssmClient::Key(Server::csp(), key, true);
555 }
556 }
557
558
559 //
560 // Verify a putative database passphrase.
561 // If the database is already unlocked, just check the passphrase.
562 // Otherwise, unlock with that passphrase and report success.
563 // Caller must hold the common lock.
564 //
565 bool Database::validatePassphrase(const CssmData &passphrase) const
566 {
567 if (common->hasMaster()) {
568 // verify against known secret
569 return common->validatePassphrase(passphrase);
570 } else {
571 // no master secret - perform "blind" unlock to avoid actual unlock
572 try {
573 DatabaseCryptoCore test;
574 test.setup(mBlob, passphrase);
575 test.decodeCore(mBlob, NULL);
576 return true;
577 } catch (...) {
578 return false;
579 }
580 }
581 }
582
583
584 //
585 // Lock this database
586 //
587 void Database::lock()
588 {
589 common->lock(false);
590 }
591
592
593 //
594 // Lock all databases we know of.
595 // This is an interim stop-gap measure, until we can work out how database
596 // state should interact with true multi-session operation.
597 //
598 void Database::lockAllDatabases(CommonMap &commons, bool forSleep)
599 {
600 StLock<Mutex> _(commons); // hold all changes to Common map
601 for (CommonMap::iterator it = commons.begin(); it != commons.end(); it++)
602 it->second->lock(true, forSleep); // lock, already holding commonLock
603 }
604
605
606 //
607 // Given a Key for this database, encode it into a blob and return it.
608 //
609 KeyBlob *Database::encodeKey(const CssmKey &key, const CssmData &pubAcl, const CssmData &privAcl)
610 {
611 unlock();
612
613 // tell the cryptocore to form the key blob
614 return common->encodeKeyCore(key, pubAcl, privAcl);
615 }
616
617
618 //
619 // Given a "blobbed" key for this database, decode it into its real
620 // key object and (re)populate its ACL.
621 //
622 void Database::decodeKey(KeyBlob *blob, CssmKey &key, void * &pubAcl, void * &privAcl)
623 {
624 unlock(); // we need our keys
625
626 common->decodeKeyCore(blob, key, pubAcl, privAcl);
627 // memory protocol: pubAcl points into blob; privAcl was allocated
628
629 activity();
630 }
631
632
633 //
634 // Modify database parameters
635 //
636 void Database::setParameters(const DBParameters &params)
637 {
638 StLock<Mutex> _(*common);
639 makeUnlocked();
640 common->mParams = params;
641 common->version++; // invalidate old blobs
642 activity();
643 secdebug("SSdb", "%p common %p(%s) set params=(%ld,%d)",
644 this, common, dbName(), params.idleTimeout, params.lockOnSleep);
645 }
646
647
648 //
649 // Retrieve database parameters
650 //
651 void Database::getParameters(DBParameters &params)
652 {
653 StLock<Mutex> _(*common);
654 makeUnlocked();
655 params = common->mParams;
656 //activity(); // getting parameters does not reset the idle timer
657 }
658
659
660 //
661 // Intercept ACL change requests and reset blob validity
662 //
663 void Database::instantiateAcl()
664 {
665 StLock<Mutex> _(*common);
666 makeUnlocked();
667 }
668
669 void Database::changedAcl()
670 {
671 StLock<Mutex> _(*common);
672 version = 0;
673 }
674
675 const Database *Database::relatedDatabase() const
676 { return this; }
677
678
679 //
680 // Debugging support
681 //
682 #if defined(DEBUGDUMP)
683
684 void Database::debugDump(const char *msg)
685 {
686 assert(common);
687 const Signature &sig = common->identifier();
688 uint32 sig4; memcpy(&sig4, sig.bytes, sizeof(sig4));
689 Debug::dump("** %s(%8.8lx) common=%p(%ld) %s\n",
690 common->dbName(), sig4, common, common->useCount, msg);
691 if (isLocked())
692 Debug::dump(" locked");
693 else {
694 Time::Absolute when = common->when();
695 time_t whenTime = time_t(when);
696 Debug::dump(" UNLOCKED(%24.24s/%.2g)", ctime(&whenTime),
697 (when - Time::now()).seconds());
698 }
699 Debug::dump(" %s blobversion=%ld/%ld %svalidData",
700 (common->isValid() ? "validkeys" : "!validkeys"),
701 version, common->version,
702 (mValidData ? "" : "!"));
703 Debug::dump(" Params=(%ld %d)\n",
704 common->mParams.idleTimeout, common->mParams.lockOnSleep);
705 }
706
707 #endif //DEBUGDUMP
708
709
710 //
711 // Database::Common basic features
712 //
713 Database::Common::Common(const DbIdentifier &id, CommonMap &commonPool)
714 : pool(commonPool), mIdentifier(id), sequence(0), useCount(0), version(1),
715 mIsLocked(true), mValidParams(false)
716 { }
717
718 Database::Common::~Common()
719 {
720 // explicitly unschedule ourselves
721 Server::active().clearTimer(this);
722 pool.erase(identifier());
723 }
724
725
726 void Database::Common::makeNewSecrets()
727 {
728 // we already have a master key (right?)
729 assert(hasMaster());
730
731 // tell crypto core to generate the use keys
732 DatabaseCryptoCore::generateNewSecrets();
733
734 // we're now officially "unlocked"; set the timer
735 mIsLocked = false;
736 activity();
737 }
738
739
740 void Database::discard(Common *common)
741 {
742 // LOCKING: pool lock held, *common NOT held
743 secdebug("SSdb", "discarding dbcommon %p (no users, locked)", common);
744 delete common;
745 }
746
747
748 //
749 // All unlocking activity ultimately funnels through this method.
750 // This unlocks a Common using the secrets setup in its crypto core
751 // component, and performs all the housekeeping needed to represent
752 // the state change.
753 //
754 bool Database::Common::unlock(DbBlob *blob, void **privateAclBlob)
755 {
756 try {
757 // Tell the cryptocore to (try to) decode itself. This will fail
758 // in an astonishing variety of ways if the passphrase is wrong.
759 assert(hasMaster());
760 decodeCore(blob, privateAclBlob);
761 secdebug("SSdb", "%p unlock successful", this);
762 } catch (...) {
763 secdebug("SSdb", "%p unlock failed", this);
764 return false;
765 }
766
767 // get the database parameters only if we haven't got them yet
768 if (!mValidParams) {
769 mParams = blob->params;
770 n2hi(mParams.idleTimeout);
771 mValidParams = true; // sticky
772 }
773
774 // now successfully unlocked
775 mIsLocked = false;
776
777 // set timeout
778 activity();
779
780 // broadcast unlock notification
781 KeychainNotifier::unlock(identifier());
782 return true;
783 }
784
785
786 void Database::Common::lock(bool holdingCommonLock, bool forSleep)
787 {
788 StLock<Mutex> locker(*this);
789 if (!isLocked()) {
790 if (forSleep && !mParams.lockOnSleep)
791 return; // it doesn't want to
792
793 mIsLocked = true;
794 DatabaseCryptoCore::invalidate();
795 KeychainNotifier::lock(identifier());
796 Server::active().clearTimer(this);
797
798 // if no database refers to us now, we're history
799 StLock<Mutex> _(pool, false);
800 if (!holdingCommonLock)
801 _.lock();
802 if (useCount == 0) {
803 locker.unlock(); // release object lock
804 discard(this);
805 }
806 }
807 }
808
809 DbBlob *Database::Common::encode(Database &db)
810 {
811 assert(!isLocked()); // must have been unlocked by caller
812
813 // export database ACL to blob form
814 CssmData pubAcl, privAcl;
815 db.exportBlob(pubAcl, privAcl);
816
817 // tell the cryptocore to form the blob
818 DbBlob form;
819 form.randomSignature = identifier();
820 form.sequence = sequence;
821 form.params = mParams;
822 h2ni(form.params.idleTimeout);
823
824 assert(hasMaster());
825 DbBlob *blob = encodeCore(form, pubAcl, privAcl);
826
827 // clean up and go
828 db.allocator.free(pubAcl);
829 db.allocator.free(privAcl);
830 return blob;
831 }
832
833
834 //
835 // Perform deferred lock processing for a database.
836 //
837 void Database::Common::action()
838 {
839 secdebug("SSdb", "common %s(%p) locked by timer (%d refs)",
840 dbName(), this, int(useCount));
841 lock(false);
842 }
843
844 void Database::Common::activity()
845 {
846 if (!isLocked())
847 Server::active().setTimer(this, Time::Interval(int(mParams.idleTimeout)));
848 }
849
850
851 //
852 // Implementation of a "system keychain unlock key store"
853 //
854 SystemKeychainKey::SystemKeychainKey(const char *path)
855 : mPath(path)
856 {
857 // explicitly set up a key header for a raw 3DES key
858 CssmKey::Header &hdr = mKey.header();
859 hdr.blobType(CSSM_KEYBLOB_RAW);
860 hdr.blobFormat(CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING);
861 hdr.keyClass(CSSM_KEYCLASS_SESSION_KEY);
862 hdr.algorithm(CSSM_ALGID_3DES_3KEY_EDE);
863 hdr.KeyAttr = 0;
864 hdr.KeyUsage = CSSM_KEYUSE_ANY;
865 mKey = CssmData::wrap(mBlob.masterKey);
866 }
867
868 SystemKeychainKey::~SystemKeychainKey()
869 {
870 }
871
872 bool SystemKeychainKey::matches(const DbBlob::Signature &signature)
873 {
874 return update() && signature == mBlob.signature;
875 }
876
877 bool SystemKeychainKey::update()
878 {
879 // if we checked recently, just assume it's okay
880 if (mUpdateThreshold > Time::now())
881 return mValid;
882
883 // check the file
884 struct stat st;
885 if (::stat(mPath.c_str(), &st)) {
886 // something wrong with the file; can't use it
887 mUpdateThreshold = Time::now() + Time::Interval(checkDelay);
888 return mValid = false;
889 }
890 if (mValid && Time::Absolute(st.st_mtimespec) == mCachedDate)
891 return true;
892 mUpdateThreshold = Time::now() + Time::Interval(checkDelay);
893
894 try {
895 secdebug("syskc", "reading system unlock record from %s", mPath.c_str());
896 AutoFileDesc fd(mPath, O_RDONLY);
897 if (fd.read(mBlob) != sizeof(mBlob))
898 return false;
899 if (mBlob.isValid()) {
900 mCachedDate = st.st_mtimespec;
901 return mValid = true;
902 } else
903 return mValid = false;
904 } catch (...) {
905 secdebug("syskc", "system unlock record not available");
906 return false;
907 }
908 }