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