2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 // kcdatabase - software database container implementation.
28 // General implementation notes:
29 // This leverages LocalDatabase/LocalKey for cryptography, and adds the
30 // storage coder/decoder logic that implements "keychain" databases in their
31 // intricately choreographed dance between securityd and the AppleCSPDL.
32 // As always, Database objects are lifetime-bound to their Process referent;
33 // they can also be destroyed explicitly with a client release call.
34 // DbCommons are reference-held by their Databases, with one extra special
35 // reference (from the Session) introduced when the database unlocks, and
36 // removed when it locks again. That way, an unused DbCommon dies when it
37 // is locked or when the Session dies, whichever happens earlier.
38 // There is (as yet) no global-scope Database object for Keychain databases.
40 #include "kcdatabase.h"
41 #include "agentquery.h"
45 #include "notifications.h"
46 #include <vector> // @@@ 4003540 workaround
47 #include <security_agent_client/agentclient.h>
48 #include <security_cdsa_utilities/acl_any.h> // for default owner ACLs
49 #include <security_cdsa_client/wrapkey.h>
50 #include <security_cdsa_client/genkey.h>
51 #include <security_cdsa_client/signclient.h>
52 #include <security_cdsa_client/cryptoclient.h>
53 #include <security_cdsa_client/macclient.h>
54 #include <securityd_client/dictionary.h>
55 #include <security_utilities/endian.h>
59 // Create a Database object from initial parameters (create operation)
61 KeychainDatabase::KeychainDatabase(const DLDbIdentifier
&id
, const DBParameters
¶ms
, Process
&proc
,
62 const AccessCredentials
*cred
, const AclEntryPrototype
*owner
)
63 : LocalDatabase(proc
), mValidData(false), version(0), mBlob(NULL
)
65 // save a copy of the credentials for later access control
66 mCred
= DataWalkers::copy(cred
, Allocator::standard());
68 // create a new random signature to complete the DLDbIdentifier
69 DbBlob::Signature newSig
;
70 Server::active().random(newSig
.bytes
);
71 DbIdentifier
ident(id
, newSig
);
73 // create common block and initialize
74 RefPointer
<KeychainDbCommon
> newCommon
= new KeychainDbCommon(proc
.session(), ident
);
75 StLock
<Mutex
> _(*newCommon
);
77 // new common is now visible (in ident-map) but we hold its lock
79 // establish the new master secret
80 establishNewSecrets(cred
, SecurityAgent::newDatabase
);
82 // set initial database parameters
83 common().mParams
= params
;
85 // the common is "unlocked" now
86 common().makeNewSecrets();
88 // establish initial ACL
90 acl().cssmSetInitial(*owner
);
92 acl().cssmSetInitial(new AnyAclSubject());
95 // for now, create the blob immediately
98 proc
.addReference(*this);
100 // this new keychain is unlocked; make it so
103 secdebug("KCdb", "database %s(%p) created, common at %p",
104 common().dbName(), this, &common());
109 // Create a Database object from a database blob (decoding)
111 KeychainDatabase::KeychainDatabase(const DLDbIdentifier
&id
, const DbBlob
*blob
, Process
&proc
,
112 const AccessCredentials
*cred
)
113 : LocalDatabase(proc
), mValidData(false), version(0)
117 // save a copy of the credentials for later access control
118 mCred
= DataWalkers::copy(cred
, Allocator::standard());
119 mBlob
= blob
->copy();
121 // check to see if we already know about this database
122 DbIdentifier
ident(id
, blob
->randomSignature
);
123 Session
&session
= process().session();
124 StLock
<Mutex
> _(session
);
125 if (KeychainDbCommon
*dbcom
=
126 session
.findFirst
<KeychainDbCommon
, const DbIdentifier
&>(&KeychainDbCommon::identifier
, ident
)) {
128 //@@@ arbitrate sequence number here, perhaps update common().mParams
130 "open database %s(%p) version %lx at known common %p",
131 common().dbName(), this, blob
->version(), &common());
133 // DbCommon not present; make a new one
134 parent(*new KeychainDbCommon(proc
.session(), ident
));
135 common().mParams
= blob
->params
;
136 secdebug("KCdb", "open database %s(%p) version %lx with new common %p",
137 common().dbName(), this, blob
->version(), &common());
138 // this DbCommon is locked; no timer or reference setting
140 proc
.addReference(*this);
145 // Special-purpose constructor for keychain synchronization. Copies an
146 // existing keychain but uses the operational keys from secretsBlob. The
147 // new KeychainDatabase will silently replace the existing KeychainDatabase
148 // as soon as the client declares that re-encoding of all keychain items is
149 // finished. This is a little perilous since it allows a client to dictate
150 // securityd state, but we try to ensure that only the client that started
151 // the re-encoding can declare it done.
153 KeychainDatabase::KeychainDatabase(KeychainDatabase
&src
, Process
&proc
,
154 const DbBlob
*secretsBlob
, const CssmData
&agentData
)
155 : LocalDatabase(proc
), mValidData(false), version(0), mBlob(NULL
)
157 validateBlob(secretsBlob
);
159 // get the passphrase to unlock secretsBlob
160 QueryDBBlobSecret query
;
161 query
.inferHints(proc
);
162 query
.addHint(AGENT_HINT_KCSYNC_DICT
, agentData
.data(), agentData
.length());
163 DatabaseCryptoCore keysCore
;
164 if (query(keysCore
, secretsBlob
) != SecurityAgent::noReason
)
165 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
166 // keysCore is now ready to yield its secrets to us
168 mCred
= DataWalkers::copy(src
.mCred
, Allocator::standard());
170 // Give this KeychainDatabase a temporary name
171 std::string newDbName
= std::string("////") + std::string(src
.identifier().dbName());
172 DLDbIdentifier
newDLDbIdent(src
.identifier().dlDbIdentifier().ssuid(), newDbName
.c_str(), src
.identifier().dlDbIdentifier().dbLocation());
173 DbIdentifier
ident(newDLDbIdent
, src
.identifier());
175 // create common block and initialize
176 RefPointer
<KeychainDbCommon
> newCommon
= new KeychainDbCommon(proc
.session(), ident
);
177 StLock
<Mutex
> _(*newCommon
);
180 // set initial database parameters from the source keychain
181 common().mParams
= src
.common().mParams
;
183 // establish the source keychain's master secret as ours
184 // @@@ NB: this is a v. 0.1 assumption. We *should* trigger new UI
185 // that offers the user the option of using the existing password
186 // or choosing a new one. That would require a new
187 // SecurityAgentQuery type, new UI, and--possibly--modifications to
188 // ensure that the new password is available here to generate the
189 // new master secret.
190 src
.unlockDb(); // precaution for masterKey()
191 common().setup(src
.blob(), src
.common().masterKey());
193 // import the operational secrets
194 common().importSecrets(keysCore
);
196 // import source keychain's ACL
197 CssmData pubAcl
, privAcl
;
198 src
.acl().exportBlob(pubAcl
, privAcl
);
199 importBlob(pubAcl
.data(), privAcl
.data());
200 src
.acl().allocator
.free(pubAcl
);
201 src
.acl().allocator
.free(privAcl
);
203 // indicate that this keychain should be allowed to do some otherwise
204 // risky things required for copying, like re-encoding keys
205 mRecodingSource
= &src
;
207 common().setUnlocked();
212 proc
.addReference(*this);
213 secdebug("SSdb", "database %s(%p) created as copy, common at %p",
214 common().dbName(), this, &common());
219 // Destroy a Database
221 KeychainDatabase::~KeychainDatabase()
223 secdebug("KCdb", "deleting database %s(%p) common %p",
224 common().dbName(), this, &common());
225 Allocator::standard().free(mCred
);
226 Allocator::standard().free(mBlob
);
231 // Basic Database virtual implementations
233 KeychainDbCommon
&KeychainDatabase::common() const
235 return parent
<KeychainDbCommon
>();
238 const char *KeychainDatabase::dbName() const
240 return common().dbName();
243 bool KeychainDatabase::transient() const
245 return false; // has permanent store
248 AclKind
KeychainDatabase::aclKind() const
253 Database
*KeychainDatabase::relatedDatabase()
259 static inline KeychainKey
&myKey(Key
*key
)
261 return *safe_cast
<KeychainKey
*>(key
);
266 // (Re-)Authenticate the database. This changes the stored credentials.
268 void KeychainDatabase::authenticate(CSSM_DB_ACCESS_TYPE mode
,
269 const AccessCredentials
*cred
)
271 StLock
<Mutex
> _(common());
273 // the (Apple specific) RESET bit means "lock the database now"
275 case CSSM_DB_ACCESS_RESET
:
276 secdebug("KCdb", "%p ACCESS_RESET triggers keychain lock", this);
280 // store the new credentials for future use
281 secdebug("KCdb", "%p authenticate stores new database credentials", this);
282 AccessCredentials
*newCred
= DataWalkers::copy(cred
, Allocator::standard());
283 Allocator::standard().free(mCred
);
290 // Make a new KeychainKey.
291 // If PERMANENT is off, make a temporary key instead.
292 // The db argument allows you to create for another KeychainDatabase (only);
293 // it defaults to ourselves.
295 RefPointer
<Key
> KeychainDatabase::makeKey(Database
&db
, const CssmKey
&newKey
,
296 uint32 moreAttributes
, const AclEntryPrototype
*owner
)
299 if (moreAttributes
& CSSM_KEYATTR_PERMANENT
)
300 return new KeychainKey(db
, newKey
, moreAttributes
, owner
);
302 return process().makeTemporaryKey(newKey
, moreAttributes
, owner
);
305 RefPointer
<Key
> KeychainDatabase::makeKey(const CssmKey
&newKey
,
306 uint32 moreAttributes
, const AclEntryPrototype
*owner
)
308 return makeKey(*this, newKey
, moreAttributes
, owner
);
313 // Return the database blob, recalculating it as needed.
315 DbBlob
*KeychainDatabase::blob()
317 StLock
<Mutex
> _(common());
319 makeUnlocked(); // unlock to get master secret
320 encode(); // (re)encode blob if needed
322 activity(); // reset timeout
323 assert(validBlob()); // better have a valid blob now...
329 // Encode the current database as a blob.
330 // Note that this returns memory we own and keep.
331 // Caller must hold common lock.
333 void KeychainDatabase::encode()
335 DbBlob
*blob
= common().encode(*this);
336 Allocator::standard().free(mBlob
);
338 version
= common().version
;
339 secdebug("KCdb", "encoded database %p common %p(%s) version %ld params=(%ld,%d)",
340 this, &common(), dbName(), version
,
341 common().mParams
.idleTimeout
, common().mParams
.lockOnSleep
);
346 // Change the passphrase on a database
348 void KeychainDatabase::changePassphrase(const AccessCredentials
*cred
)
350 // get and hold the common lock (don't let other threads break in here)
351 StLock
<Mutex
> _(common());
353 // establish OLD secret - i.e. unlock the database
354 //@@@ do we want to leave the final lock state alone?
357 // establish NEW secret
358 establishNewSecrets(cred
, SecurityAgent::changePassphrase
);
359 common().invalidateBlob(); // blob state changed
360 secdebug("KCdb", "Database %s(%p) master secret changed", common().dbName(), this);
361 encode(); // force rebuild of local blob
363 // send out a notification
364 notify(kNotificationEventPassphraseChanged
);
366 // I guess this counts as an activity
371 // Second stage of keychain synchronization: overwrite the original keychain's
372 // (this KeychainDatabase's) operational secrets
374 void KeychainDatabase::commitSecretsForSync(KeychainDatabase
&cloneDb
)
376 StLock
<Mutex
> _(common());
378 // try to detect spoofing
379 if (cloneDb
.mRecodingSource
!= this)
380 CssmError::throwMe(CSSM_ERRCODE_INVALID_DB_HANDLE
);
382 // in case we autolocked since starting the sync
386 // Decode all keys whose handles refer to this on-disk keychain so that
387 // if the holding client commits the key back to disk, it's encoded with
388 // the new operational secrets. The recoding client *must* hold a write
389 // lock for the on-disk keychain from the moment it starts recoding key
390 // items until after this call.
392 // @@@ This specific implementation is a workaround for 4003540.
393 std::vector
<CSSM_HANDLE
> handleList
;
394 HandleObject::findAllRefs
<KeychainKey
>(handleList
);
395 size_t count
= handleList
.size();
397 for (unsigned int n
= 0; n
< count
; ++n
) {
398 RefPointer
<KeychainKey
> kckey
=
399 HandleObject::findRefAndLock
<KeychainKey
>(handleList
[n
], CSSMERR_CSP_INVALID_KEY_REFERENCE
);
400 StLock
<Mutex
> _(*kckey
/*, true*/);
401 if (kckey
->database().global().identifier() == identifier()) {
402 kckey
->key(); // force decode
403 kckey
->invalidateBlob();
404 secdebug("kcrecode", "changed extant key %p (proc %d)",
405 &*kckey
, kckey
->process().pid());
410 // it is now safe to replace the old op secrets
411 common().importSecrets(cloneDb
.common());
412 common().invalidateBlob();
417 // Extract the database master key as a proper Key object.
419 RefPointer
<Key
> KeychainDatabase::extractMasterKey(Database
&db
,
420 const AccessCredentials
*cred
, const AclEntryPrototype
*owner
,
421 uint32 usage
, uint32 attrs
)
423 // get and hold common lock
424 StLock
<Mutex
> _(common());
426 // force lock to require re-validation of credentials
429 // unlock to establish master secret
432 // extract the raw cryptographic key
433 CssmClient::WrapKey
wrap(Server::csp(), CSSM_ALGID_NONE
);
435 wrap(common().masterKey(), key
);
437 // make the key object and return it
438 return makeKey(db
, key
, attrs
& LocalKey::managedAttributes
, owner
);
443 // Unlock this database (if needed) by obtaining the master secret in some
444 // suitable way and then proceeding to unlock with it.
445 // Does absolutely nothing if the database is already unlocked.
446 // The makeUnlocked forms are identical except the assume the caller already
447 // holds the common lock.
449 void KeychainDatabase::unlockDb()
451 StLock
<Mutex
> _(common());
455 void KeychainDatabase::makeUnlocked()
457 return makeUnlocked(mCred
);
460 void KeychainDatabase::makeUnlocked(const AccessCredentials
*cred
)
463 secdebug("KCdb", "%p(%p) unlocking for makeUnlocked()", this, &common());
464 assert(mBlob
|| (mValidData
&& common().hasMaster()));
465 establishOldSecrets(cred
);
466 common().setUnlocked(); // mark unlocked
467 } else if (!mValidData
) { // need to decode to get our ACLs, master secret available
468 secdebug("KCdb", "%p(%p) is unlocked; decoding for makeUnlocked()", this, &common());
470 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
478 // The following unlock given an explicit passphrase, rather than using
479 // (special cred sample based) default procedures.
481 void KeychainDatabase::unlockDb(const CssmData
&passphrase
)
483 StLock
<Mutex
> _(common());
484 makeUnlocked(passphrase
);
487 void KeychainDatabase::makeUnlocked(const CssmData
&passphrase
)
490 if (decode(passphrase
))
493 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
494 } else if (!mValidData
) { // need to decode to get our ACLs, passphrase available
496 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
504 // Nonthrowing passphrase-based unlock. This returns false if unlock failed.
505 // Note that this requires an explicitly given passphrase.
506 // Caller must hold common lock.
508 bool KeychainDatabase::decode(const CssmData
&passphrase
)
511 common().setup(mBlob
, passphrase
);
517 // Given the established master secret, decode the working keys and other
518 // functional secrets for this database. Return false (do NOT throw) if
519 // the decode fails. Call this in low(er) level code once you established
522 bool KeychainDatabase::decode()
525 assert(common().hasMaster());
526 void *privateAclBlob
;
527 if (common().unlockDb(mBlob
, &privateAclBlob
)) {
529 acl().importBlob(mBlob
->publicAclBlob(), privateAclBlob
);
532 Allocator::standard().free(privateAclBlob
);
535 secdebug("KCdb", "%p decode failed", this);
541 // Given an AccessCredentials for this database, wring out the existing primary
542 // database secret by whatever means necessary.
543 // On entry, caller must hold the database common lock. It will be held
544 // throughout except when user interaction is required. User interaction
545 // requires relinquishing the database common lock and taking the UI lock. On
546 // return from user interaction, the UI lock is relinquished and the database
547 // common lock must be reacquired. At no time may the caller hold both locks.
548 // On exit, the crypto core has its master secret. If things go wrong,
549 // we will throw a suitable exception. Note that encountering any malformed
550 // credential sample will throw, but this is not guaranteed -- don't assume
551 // that NOT throwing means creds is entirely well-formed (it may just be good
552 // enough to work THIS time).
555 // Walk through the creds. Fish out those credentials (in order) that
556 // are for unlock processing (they have no ACL subject correspondents),
557 // and (try to) obey each in turn, until one produces a valid secret
558 // or you run out. If no special samples are found at all, interpret that as
559 // "use the system global default," which happens to be hard-coded right here.
561 void KeychainDatabase::establishOldSecrets(const AccessCredentials
*creds
)
563 list
<CssmSample
> samples
;
564 if (creds
&& creds
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
, samples
)) {
565 for (list
<CssmSample
>::iterator it
= samples
.begin(); it
!= samples
.end(); it
++) {
566 TypedList
&sample
= *it
;
567 sample
.checkProper();
568 switch (sample
.type()) {
569 // interactively prompt the user - no additional data
570 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
:
572 secdebug("KCdb", "%p attempting interactive unlock", this);
573 QueryUnlock
query(*this);
574 // Holding DB common lock during UI will deadlock securityd
575 StSyncLock
<Mutex
, Mutex
> uisync(common().uiLock(), common());
576 query
.inferHints(Server::process());
577 if (query() == SecurityAgent::noReason
)
581 // try to use an explicitly given passphrase - Data:passphrase
582 case CSSM_SAMPLE_TYPE_PASSWORD
:
583 if (sample
.length() != 2)
584 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
585 secdebug("KCdb", "%p attempting passphrase unlock", this);
586 if (decode(sample
[1]))
589 // try to open with a given master key - Data:CSP or KeyHandle, Data:CssmKey
590 case CSSM_WORDID_SYMMETRIC_KEY
:
592 secdebug("KCdb", "%p attempting explicit key unlock", this);
593 common().setup(mBlob
, keyFromCreds(sample
, 4));
597 // explicitly defeat the default action but don't try anything in particular
598 case CSSM_WORDID_CANCELED
:
599 secdebug("KCdb", "%p defeat default action", this);
602 // Unknown sub-sample for unlocking.
603 // If we wanted to be fascist, we could now do
604 // CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
605 // But instead we try to be tolerant and continue on.
606 // This DOES however count as an explicit attempt at specifying unlock,
607 // so we will no longer try the default case below...
608 secdebug("KCdb", "%p unknown sub-sample unlock (%ld) ignored", this, sample
.type());
616 // attempt system-keychain unlock
617 SystemKeychainKey
systemKeychain(kSystemUnlockFile
);
618 if (systemKeychain
.matches(mBlob
->randomSignature
)) {
619 secdebug("KCdb", "%p attempting system unlock", this);
620 common().setup(mBlob
, CssmClient::Key(Server::csp(), systemKeychain
.key(), true));
625 QueryUnlock
query(*this);
626 // attempt interactive unlock
627 StSyncLock
<Mutex
, Mutex
> uisync(common().uiLock(), common());
628 query
.inferHints(Server::process());
629 if (query() == SecurityAgent::noReason
)
633 // out of options - no secret obtained
634 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
639 // Same thing, but obtain a new secret somehow and set it into the common.
641 void KeychainDatabase::establishNewSecrets(const AccessCredentials
*creds
, SecurityAgent::Reason reason
)
643 list
<CssmSample
> samples
;
644 if (creds
&& creds
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
, samples
)) {
645 for (list
<CssmSample
>::iterator it
= samples
.begin(); it
!= samples
.end(); it
++) {
646 TypedList
&sample
= *it
;
647 sample
.checkProper();
648 switch (sample
.type()) {
649 // interactively prompt the user
650 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
:
652 secdebug("KCdb", "%p specified interactive passphrase", this);
653 QueryNewPassphrase
query(*this, reason
);
654 StSyncLock
<Mutex
, Mutex
> uisync(common().uiLock(), common());
655 query
.inferHints(Server::process());
656 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
657 if (query(passphrase
) == SecurityAgent::noReason
) {
658 common().setup(NULL
, passphrase
);
663 // try to use an explicitly given passphrase
664 case CSSM_SAMPLE_TYPE_PASSWORD
:
665 secdebug("KCdb", "%p specified explicit passphrase", this);
666 if (sample
.length() != 2)
667 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
668 common().setup(NULL
, sample
[1]);
670 // try to open with a given master key
671 case CSSM_WORDID_SYMMETRIC_KEY
:
672 secdebug("KCdb", "%p specified explicit master key", this);
673 common().setup(NULL
, keyFromCreds(sample
, 3));
675 // explicitly defeat the default action but don't try anything in particular
676 case CSSM_WORDID_CANCELED
:
677 secdebug("KCdb", "%p defeat default action", this);
680 // Unknown sub-sample for acquiring new secret.
681 // If we wanted to be fascist, we could now do
682 // CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
683 // But instead we try to be tolerant and continue on.
684 // This DOES however count as an explicit attempt at specifying unlock,
685 // so we will no longer try the default case below...
686 secdebug("KCdb", "%p unknown sub-sample acquisition (%ld) ignored",
687 this, sample
.type());
692 // default action -- interactive (only)
693 QueryNewPassphrase
query(*this, reason
);
694 StSyncLock
<Mutex
, Mutex
> uisync(common().uiLock(), common());
695 query
.inferHints(Server::process());
696 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
697 if (query(passphrase
) == SecurityAgent::noReason
) {
698 common().setup(NULL
, passphrase
);
703 // out of options - no secret obtained
704 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
709 // Given a (truncated) Database credentials TypedList specifying a master key,
710 // locate the key and return a reference to it.
712 CssmClient::Key
KeychainDatabase::keyFromCreds(const TypedList
&sample
, unsigned int requiredLength
)
714 // decode TypedList structure (sample type; Data:CSPHandle; Data:CSSM_KEY)
715 assert(sample
.type() == CSSM_WORDID_SYMMETRIC_KEY
);
716 if (sample
.length() != requiredLength
717 || sample
[1].type() != CSSM_LIST_ELEMENT_DATUM
718 || sample
[2].type() != CSSM_LIST_ELEMENT_DATUM
719 || (requiredLength
== 4 && sample
[3].type() != CSSM_LIST_ELEMENT_DATUM
))
720 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
721 CSSM_CSP_HANDLE
&handle
= *sample
[1].data().interpretedAs
<CSSM_CSP_HANDLE
>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
722 CssmKey
&key
= *sample
[2].data().interpretedAs
<CssmKey
>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
724 if (key
.header().cspGuid() == gGuidAppleCSPDL
) {
725 // handleOrKey is a SecurityServer KeyHandle; ignore key argument
726 return safer_cast
<LocalKey
&>(*Server::key(handle
));
728 // not a KeyHandle reference; use key as a raw key
729 if (key
.header().blobType() != CSSM_KEYBLOB_RAW
)
730 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE
);
731 if (key
.header().keyClass() != CSSM_KEYCLASS_SESSION_KEY
)
732 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
733 return CssmClient::Key(Server::csp(), key
, true);
739 // Verify a putative database passphrase.
740 // If the database is already unlocked, just check the passphrase.
741 // Otherwise, unlock with that passphrase and report success.
742 // Caller must hold the common lock.
744 bool KeychainDatabase::validatePassphrase(const CssmData
&passphrase
) const
746 if (common().hasMaster()) {
747 // verify against known secret
748 return common().validatePassphrase(passphrase
);
750 // no master secret - perform "blind" unlock to avoid actual unlock
752 DatabaseCryptoCore test
;
753 test
.setup(mBlob
, passphrase
);
754 test
.decodeCore(mBlob
, NULL
);
764 // Lock this database
766 void KeychainDatabase::lockDb()
773 // Given a Key for this database, encode it into a blob and return it.
775 KeyBlob
*KeychainDatabase::encodeKey(const CssmKey
&key
, const CssmData
&pubAcl
, const CssmData
&privAcl
)
779 // tell the cryptocore to form the key blob
780 return common().encodeKeyCore(key
, pubAcl
, privAcl
);
785 // Given a "blobbed" key for this database, decode it into its real
786 // key object and (re)populate its ACL.
788 void KeychainDatabase::decodeKey(KeyBlob
*blob
, CssmKey
&key
, void * &pubAcl
, void * &privAcl
)
790 unlockDb(); // we need our keys
792 common().decodeKeyCore(blob
, key
, pubAcl
, privAcl
);
793 // memory protocol: pubAcl points into blob; privAcl was allocated
799 // Given a KeychainKey (that implicitly belongs to another keychain),
800 // return it encoded using this keychain's operational secrets.
802 KeyBlob
*KeychainDatabase::recodeKey(KeychainKey
&oldKey
)
804 if (mRecodingSource
!= &oldKey
.referent
<KeychainDatabase
>()) {
805 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
807 oldKey
.instantiateAcl(); // make sure key is decoded
808 CssmData publicAcl
, privateAcl
;
809 oldKey
.exportBlob(publicAcl
, privateAcl
);
810 // NB: blob's memory belongs to caller, not the common
811 KeyBlob
*blob
= common().encodeKeyCore(oldKey
.cssmKey(), publicAcl
, privateAcl
);
812 oldKey
.acl().allocator
.free(publicAcl
);
813 oldKey
.acl().allocator
.free(privateAcl
);
819 // Modify database parameters
821 void KeychainDatabase::setParameters(const DBParameters
¶ms
)
823 StLock
<Mutex
> _(common());
825 common().mParams
= params
;
826 common().invalidateBlob(); // invalidate old blobs
827 activity(); // (also resets the timeout timer)
828 secdebug("KCdb", "%p common %p(%s) set params=(%ld,%d)",
829 this, &common(), dbName(), params
.idleTimeout
, params
.lockOnSleep
);
834 // Retrieve database parameters
836 void KeychainDatabase::getParameters(DBParameters
¶ms
)
838 StLock
<Mutex
> _(common());
840 params
= common().mParams
;
841 //activity(); // getting parameters does not reset the idle timer
846 // RIGHT NOW, database ACLs are attached to the database.
847 // This will soon move upstairs.
849 SecurityServerAcl
&KeychainDatabase::acl()
856 // Intercept ACL change requests and reset blob validity
858 void KeychainDatabase::instantiateAcl()
860 StLock
<Mutex
> _(common());
864 void KeychainDatabase::changedAcl()
866 StLock
<Mutex
> _(common());
872 // Check an incoming DbBlob for basic viability
874 void KeychainDatabase::validateBlob(const DbBlob
*blob
)
876 // perform basic validation on the blob
878 blob
->validate(CSSMERR_APPLEDL_INVALID_DATABASE_BLOB
);
879 switch (blob
->version()) {
880 #if defined(COMPAT_OSX_10_0)
881 case blob
->version_MacOS_10_0
:
884 case blob
->version_MacOS_10_1
:
887 CssmError::throwMe(CSSMERR_APPLEDL_INCOMPATIBLE_DATABASE_BLOB
);
895 #if defined(DEBUGDUMP)
897 void KeychainDbCommon::dumpNode()
899 PerSession::dumpNode();
900 uint32 sig
; memcpy(&sig
, &mIdentifier
.signature(), sizeof(sig
));
901 Debug::dump(" %s[%8.8lx]", mIdentifier
.dbName(), sig
);
903 Debug::dump(" locked");
905 time_t whenTime
= time_t(when());
906 Debug::dump(" unlocked(%24.24s/%.2g)", ctime(&whenTime
),
907 (when() - Time::now()).seconds());
909 Debug::dump(" params=(%ld,%d)", mParams
.idleTimeout
, mParams
.lockOnSleep
);
912 void KeychainDatabase::dumpNode()
914 PerProcess::dumpNode();
915 Debug::dump(" %s vers=%ld",
916 mValidData
? " data" : " nodata", version
);
918 uint32 sig
; memcpy(&sig
, &mBlob
->randomSignature
, sizeof(sig
));
919 Debug::dump(" blob=%p[%8.8lx]", mBlob
, sig
);
921 Debug::dump(" noblob");
929 // DbCommon basic features
931 KeychainDbCommon::KeychainDbCommon(Session
&ssn
, const DbIdentifier
&id
)
932 : LocalDbCommon(ssn
), sequence(0), version(1), mIdentifier(id
),
933 mIsLocked(true), mValidParams(false)
935 // match existing DbGlobal or create a new one
936 Server
&server
= Server::active();
937 StLock
<Mutex
> _(server
);
938 if (KeychainDbGlobal
*dbglobal
=
939 server
.findFirst
<KeychainDbGlobal
, const DbIdentifier
&>(&KeychainDbGlobal::identifier
, identifier())) {
941 secdebug("KCdb", "%p linking to existing DbGlobal %p", this, dbglobal
);
943 // DbGlobal not present; make a new one
944 parent(*new KeychainDbGlobal(identifier()));
945 secdebug("KCdb", "%p linking to new DbGlobal %p", this, &global());
948 // link lifetime to the Session
949 session().addReference(*this);
952 KeychainDbCommon::~KeychainDbCommon()
954 secdebug("KCdb", "DbCommon %p destroyed", this);
956 // explicitly unschedule ourselves
957 Server::active().clearTimer(this);
960 KeychainDbGlobal
&KeychainDbCommon::global() const
962 return parent
<KeychainDbGlobal
>();
966 void KeychainDbCommon::makeNewSecrets()
968 // we already have a master key (right?)
971 // tell crypto core to generate the use keys
972 DatabaseCryptoCore::generateNewSecrets();
974 // we're now officially "unlocked"; set the timer
980 // All unlocking activity ultimately funnels through this method.
981 // This unlocks a DbCommon using the secrets setup in its crypto core
982 // component, and performs all the housekeeping needed to represent
984 // Returns true if unlock was successful, false if it failed due to
985 // invalid/insufficient secrets. Throws on other errors.
987 bool KeychainDbCommon::unlockDb(DbBlob
*blob
, void **privateAclBlob
)
990 // Tell the cryptocore to (try to) decode itself. This will fail
991 // in an astonishing variety of ways if the passphrase is wrong.
993 decodeCore(blob
, privateAclBlob
);
994 secdebug("KCdb", "%p unlock successful", this);
996 secdebug("KCdb", "%p unlock failed", this);
1000 // get the database parameters only if we haven't got them yet
1001 if (!mValidParams
) {
1002 mParams
= blob
->params
;
1003 n2hi(mParams
.idleTimeout
);
1004 mValidParams
= true; // sticky
1007 setUnlocked(); // mark unlocked
1009 // broadcast unlock notification
1010 notify(kNotificationEventUnlocked
);
1014 void KeychainDbCommon::setUnlocked()
1016 session().addReference(*this); // active/held
1017 mIsLocked
= false; // mark unlocked
1018 activity(); // set timeout timer
1022 void KeychainDbCommon::lockDb()
1024 StLock
<Mutex
> _(*this);
1026 secdebug("KCdb", "common %s(%p) locking", dbName(), this);
1027 DatabaseCryptoCore::invalidate();
1028 notify(kNotificationEventLocked
);
1029 Server::active().clearTimer(this);
1031 mIsLocked
= true; // mark locked
1033 // this call may destroy us if we have no databases anymore
1034 session().removeReference(*this);
1039 DbBlob
*KeychainDbCommon::encode(KeychainDatabase
&db
)
1041 assert(!isLocked()); // must have been unlocked by caller
1043 // export database ACL to blob form
1044 CssmData pubAcl
, privAcl
;
1045 db
.acl().exportBlob(pubAcl
, privAcl
);
1047 // tell the cryptocore to form the blob
1049 form
.randomSignature
= identifier();
1050 form
.sequence
= sequence
;
1051 form
.params
= mParams
;
1052 h2ni(form
.params
.idleTimeout
);
1054 assert(hasMaster());
1055 DbBlob
*blob
= encodeCore(form
, pubAcl
, privAcl
);
1058 db
.acl().allocator
.free(pubAcl
);
1059 db
.acl().allocator
.free(privAcl
);
1065 // Send a keychain-related notification event about this keychain
1067 void KeychainDbCommon::notify(NotificationEvent event
)
1069 // form the data (encoded DLDbIdentifier)
1070 NameValueDictionary nvd
;
1071 NameValueDictionary::MakeNameValueDictionaryFromDLDbIdentifier(identifier(), nvd
);
1075 // inject notification into Security event system
1076 Listener::notify(kNotificationDomainDatabase
, event
, data
);
1084 // Perform deferred lock processing for a database.
1086 void KeychainDbCommon::action()
1088 secdebug("KCdb", "common %s(%p) locked by timer", dbName(), this);
1092 void KeychainDbCommon::activity()
1095 secdebug("KCdb", "setting DbCommon %p timer to %d",
1096 this, int(mParams
.idleTimeout
));
1097 Server::active().setTimer(this, Time::Interval(int(mParams
.idleTimeout
)));
1101 void KeychainDbCommon::sleepProcessing()
1103 secdebug("KCdb", "common %s(%p) sleep-lock processing", dbName(), this);
1104 StLock
<Mutex
> _(*this);
1105 if (mParams
.lockOnSleep
)
1109 void KeychainDbCommon::lockProcessing()
1116 // Keychain global objects
1118 KeychainDbGlobal::KeychainDbGlobal(const DbIdentifier
&id
)
1123 KeychainDbGlobal::~KeychainDbGlobal()
1125 secdebug("KCdb", "DbGlobal %p destroyed", this);