2 * Copyright (c) 2000-2009,2012-2014 Apple 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_cdsa_utilities/acl_any.h> // for default owner ACLs
48 #include <security_cdsa_utilities/cssmendian.h>
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>
56 #include "securityd_service/securityd_service/securityd_service_client.h"
57 #include <AssertMacros.h>
59 #include <sys/sysctl.h>
60 #include <sys/kauth.h>
62 void unflattenKey(const CssmData
&flatKey
, CssmKey
&rawKey
); //>> make static method on KeychainDatabase
64 // Process is using a cached effective uid, login window switches uid after the intial connection
65 static void get_process_euid(pid_t pid
, uid_t
* out_euid
)
67 if (!out_euid
) return;
69 struct kinfo_proc proc_info
= {};
70 int mib
[] = {CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, pid
};
71 size_t len
= sizeof(struct kinfo_proc
);
73 ret
= sysctl(mib
, (sizeof(mib
)/sizeof(int)), &proc_info
, &len
, NULL
, 0);
76 if ((ret
== 0) && (proc_info
.kp_eproc
.e_ucred
.cr_uid
!= 0)) {
77 *out_euid
= proc_info
.kp_eproc
.e_ucred
.cr_uid
;
82 unlock_keybag(KeychainDatabase
& db
, const void * secret
, int secret_len
)
86 if (!db
.common().isLoginKeychain()) return 0;
88 service_context_t context
= db
.common().session().get_current_service_context();
90 // login window attempts to change the password before a session has a uid
91 if (context
.s_uid
== AU_DEFAUDITID
) {
92 get_process_euid(db
.process().pid(), &context
.s_uid
);
95 // try to unlock first if not found then load/create or unlock
96 // loading should happen when the kb common object is created
97 // if it doesn't exist yet then the unlock will fail and we'll create everything
98 rc
= service_client_kb_unlock(&context
, secret
, secret_len
);
99 if (rc
== KB_BagNotLoaded
) {
100 if (service_client_kb_load(&context
) == KB_BagNotFound
) {
101 rc
= service_client_kb_create(&context
, secret
, secret_len
);
103 rc
= service_client_kb_unlock(&context
, secret
, secret_len
);
107 if (rc
!= 0) { // if we just upgraded make sure we swap the encryption key to the password
108 if (!db
.common().session().keybagGetState(session_keybag_check_master_key
)) {
109 CssmAutoData
encKey(Allocator::standard(Allocator::sensitive
));
110 db
.common().get_encryption_key(encKey
);
111 if ((rc
= service_client_kb_unlock(&context
, encKey
.data(), (int)encKey
.length())) == 0) {
112 rc
= service_client_kb_change_secret(&context
, encKey
.data(), (int)encKey
.length(), secret
, secret_len
);
115 if (rc
!= 0) { // if a login.keychain password exists but doesnt on the keybag update it
117 if ((secret_len
> 0) && service_client_kb_is_locked(&context
, NULL
, &no_pin
) == 0) {
119 syslog(LOG_ERR
, "Updating iCloud keychain passphrase for uid %d", context
.s_uid
);
120 service_client_kb_change_secret(&context
, NULL
, 0, secret
, secret_len
);
124 } // session_keybag_check_master_key
128 db
.common().session().keybagSetState(session_keybag_unlocked
|session_keybag_loaded
|session_keybag_check_master_key
);
130 syslog(LOG_ERR
, "Failed to unlock iCloud keychain for uid %d", context
.s_uid
);
137 change_secret_on_keybag(KeychainDatabase
& db
, const void * secret
, int secret_len
, const void * new_secret
, int new_secret_len
)
139 if (!db
.common().isLoginKeychain()) return;
141 service_context_t context
= db
.common().session().get_current_service_context();
143 // login window attempts to change the password before a session has a uid
144 if (context
.s_uid
== AU_DEFAUDITID
) {
145 get_process_euid(db
.process().pid(), &context
.s_uid
);
148 // if a login.keychain doesn't exist yet it comes into securityd as a create then change_secret
149 // we need to create the keybag in this case if it doesn't exist
150 int rc
= service_client_kb_change_secret(&context
, secret
, secret_len
, new_secret
, new_secret_len
);
151 if (rc
== KB_BagNotLoaded
) {
152 if (service_client_kb_load(&context
) == KB_BagNotFound
) {
153 rc
= service_client_kb_create(&context
, new_secret
, new_secret_len
);
155 rc
= service_client_kb_change_secret(&context
, secret
, secret_len
, new_secret
, new_secret_len
);
159 // this makes it possible to restore a deleted keybag on condition it still exists in kernel
160 if (rc
!= KB_Success
) {
161 service_client_kb_save(&context
);
164 // if for some reason we are locked lets unlock so later we don't try and throw up SecurityAgent dialog
166 if ((service_client_kb_is_locked(&context
, &locked
, NULL
) == KB_Success
) && locked
) {
167 service_client_kb_unlock(&context
, new_secret
, new_secret_len
);
172 // Create a Database object from initial parameters (create operation)
174 KeychainDatabase::KeychainDatabase(const DLDbIdentifier
&id
, const DBParameters
¶ms
, Process
&proc
,
175 const AccessCredentials
*cred
, const AclEntryPrototype
*owner
)
176 : LocalDatabase(proc
), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive
)), mSaveSecret(false), version(0), mBlob(NULL
)
178 // save a copy of the credentials for later access control
179 mCred
= DataWalkers::copy(cred
, Allocator::standard());
181 // create a new random signature to complete the DLDbIdentifier
182 DbBlob::Signature newSig
;
183 Server::active().random(newSig
.bytes
);
184 DbIdentifier
ident(id
, newSig
);
186 // create common block and initialize
187 RefPointer
<KeychainDbCommon
> newCommon
= new KeychainDbCommon(proc
.session(), ident
);
188 StLock
<Mutex
> _(*newCommon
);
190 // new common is now visible (in ident-map) but we hold its lock
192 // establish the new master secret
193 establishNewSecrets(cred
, SecurityAgent::newDatabase
);
195 // set initial database parameters
196 common().mParams
= params
;
198 // the common is "unlocked" now
199 common().makeNewSecrets();
201 // establish initial ACL
203 acl().cssmSetInitial(*owner
);
205 acl().cssmSetInitial(new AnyAclSubject());
208 // for now, create the blob immediately
211 proc
.addReference(*this);
213 // this new keychain is unlocked; make it so
216 SECURITYD_KEYCHAIN_CREATE(&common(), (char*)this->dbName(), this);
221 // Create a Database object from a database blob (decoding)
223 KeychainDatabase::KeychainDatabase(const DLDbIdentifier
&id
, const DbBlob
*blob
, Process
&proc
,
224 const AccessCredentials
*cred
)
225 : LocalDatabase(proc
), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive
)), mSaveSecret(false), version(0), mBlob(NULL
)
229 // save a copy of the credentials for later access control
230 mCred
= DataWalkers::copy(cred
, Allocator::standard());
231 mBlob
= blob
->copy();
233 // check to see if we already know about this database
234 DbIdentifier
ident(id
, blob
->randomSignature
);
235 Session
&session
= process().session();
236 if (RefPointer
<KeychainDbCommon
> dbcom
=
237 session
.findFirst
<KeychainDbCommon
, const DbIdentifier
&>(&KeychainDbCommon::identifier
, ident
)) {
239 //@@@ arbitrate sequence number here, perhaps update common().mParams
240 SECURITYD_KEYCHAIN_JOIN(&common(), (char*)this->dbName(), this);
242 // DbCommon not present; make a new one
243 parent(*new KeychainDbCommon(proc
.session(), ident
));
244 common().mParams
= blob
->params
;
245 SECURITYD_KEYCHAIN_MAKE(&common(), (char*)this->dbName(), this);
246 // this DbCommon is locked; no timer or reference setting
248 proc
.addReference(*this);
253 // Special-purpose constructor for keychain synchronization. Copies an
254 // existing keychain but uses the operational keys from secretsBlob. The
255 // new KeychainDatabase will silently replace the existing KeychainDatabase
256 // as soon as the client declares that re-encoding of all keychain items is
257 // finished. This is a little perilous since it allows a client to dictate
258 // securityd state, but we try to ensure that only the client that started
259 // the re-encoding can declare it done.
261 KeychainDatabase::KeychainDatabase(KeychainDatabase
&src
, Process
&proc
, DbHandle dbToClone
)
262 : LocalDatabase(proc
), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive
)), mSaveSecret(false), version(0), mBlob(NULL
)
264 mCred
= DataWalkers::copy(src
.mCred
, Allocator::standard());
266 // Give this KeychainDatabase a temporary name
267 std::string newDbName
= std::string("////") + std::string(src
.identifier().dbName());
268 DLDbIdentifier
newDLDbIdent(src
.identifier().dlDbIdentifier().ssuid(), newDbName
.c_str(), src
.identifier().dlDbIdentifier().dbLocation());
269 DbIdentifier
ident(newDLDbIdent
, src
.identifier());
271 // create common block and initialize
272 RefPointer
<KeychainDbCommon
> newCommon
= new KeychainDbCommon(proc
.session(), ident
);
273 StLock
<Mutex
> _(*newCommon
);
276 // set initial database parameters from the source keychain
277 common().mParams
= src
.common().mParams
;
279 // establish the source keychain's master secret as ours
280 // @@@ NB: this is a v. 0.1 assumption. We *should* trigger new UI
281 // that offers the user the option of using the existing password
282 // or choosing a new one. That would require a new
283 // SecurityAgentQuery type, new UI, and--possibly--modifications to
284 // ensure that the new password is available here to generate the
285 // new master secret.
286 src
.unlockDb(); // precaution for masterKey()
287 common().setup(src
.blob(), src
.common().masterKey());
289 // import the operational secrets
290 RefPointer
<KeychainDatabase
> srcKC
= Server::keychain(dbToClone
);
291 common().importSecrets(srcKC
->common());
293 // import source keychain's ACL
294 CssmData pubAcl
, privAcl
;
295 src
.acl().exportBlob(pubAcl
, privAcl
);
296 importBlob(pubAcl
.data(), privAcl
.data());
297 src
.acl().allocator
.free(pubAcl
);
298 src
.acl().allocator
.free(privAcl
);
300 // indicate that this keychain should be allowed to do some otherwise
301 // risky things required for copying, like re-encoding keys
302 mRecodingSource
= &src
;
304 common().setUnlocked();
309 proc
.addReference(*this);
310 secdebug("SSdb", "database %s(%p) created as copy, common at %p",
311 common().dbName(), this, &common());
315 // Destroy a Database
317 KeychainDatabase::~KeychainDatabase()
319 secdebug("KCdb", "deleting database %s(%p) common %p",
320 common().dbName(), this, &common());
321 Allocator::standard().free(mCred
);
322 Allocator::standard().free(mBlob
);
327 // Basic Database virtual implementations
329 KeychainDbCommon
&KeychainDatabase::common() const
331 return parent
<KeychainDbCommon
>();
334 const char *KeychainDatabase::dbName() const
336 return common().dbName();
339 bool KeychainDatabase::transient() const
341 return false; // has permanent store
344 AclKind
KeychainDatabase::aclKind() const
349 Database
*KeychainDatabase::relatedDatabase()
355 static inline KeychainKey
&myKey(Key
*key
)
357 return *safe_cast
<KeychainKey
*>(key
);
362 // (Re-)Authenticate the database. This changes the stored credentials.
364 void KeychainDatabase::authenticate(CSSM_DB_ACCESS_TYPE mode
,
365 const AccessCredentials
*cred
)
367 StLock
<Mutex
> _(common());
369 // the (Apple specific) RESET bit means "lock the database now"
371 case CSSM_DB_ACCESS_RESET
:
372 secdebug("KCdb", "%p ACCESS_RESET triggers keychain lock", this);
376 // store the new credentials for future use
377 secdebug("KCdb", "%p authenticate stores new database credentials", this);
378 AccessCredentials
*newCred
= DataWalkers::copy(cred
, Allocator::standard());
379 Allocator::standard().free(mCred
);
386 // Make a new KeychainKey.
387 // If PERMANENT is off, make a temporary key instead.
388 // The db argument allows you to create for another KeychainDatabase (only);
389 // it defaults to ourselves.
391 RefPointer
<Key
> KeychainDatabase::makeKey(Database
&db
, const CssmKey
&newKey
,
392 uint32 moreAttributes
, const AclEntryPrototype
*owner
)
395 if (moreAttributes
& CSSM_KEYATTR_PERMANENT
)
396 return new KeychainKey(db
, newKey
, moreAttributes
, owner
);
398 return process().makeTemporaryKey(newKey
, moreAttributes
, owner
);
401 RefPointer
<Key
> KeychainDatabase::makeKey(const CssmKey
&newKey
,
402 uint32 moreAttributes
, const AclEntryPrototype
*owner
)
404 return makeKey(*this, newKey
, moreAttributes
, owner
);
409 // Return the database blob, recalculating it as needed.
411 DbBlob
*KeychainDatabase::blob()
413 StLock
<Mutex
> _(common());
415 makeUnlocked(); // unlock to get master secret
416 encode(); // (re)encode blob if needed
418 activity(); // reset timeout
419 assert(validBlob()); // better have a valid blob now...
425 // Encode the current database as a blob.
426 // Note that this returns memory we own and keep.
427 // Caller must hold common lock.
429 void KeychainDatabase::encode()
431 DbBlob
*blob
= common().encode(*this);
432 Allocator::standard().free(mBlob
);
434 version
= common().version
;
435 secdebug("KCdb", "encoded database %p common %p(%s) version %u params=(%u,%u)",
436 this, &common(), dbName(), version
,
437 common().mParams
.idleTimeout
, common().mParams
.lockOnSleep
);
442 // Change the passphrase on a database
444 void KeychainDatabase::changePassphrase(const AccessCredentials
*cred
)
446 // get and hold the common lock (don't let other threads break in here)
447 StLock
<Mutex
> _(common());
449 // establish OLD secret - i.e. unlock the database
450 //@@@ do we want to leave the final lock state alone?
451 if (common().isLoginKeychain()) mSaveSecret
= true;
454 // establish NEW secret
455 if(!establishNewSecrets(cred
, SecurityAgent::changePassphrase
)) {
456 secdebug("KCdb", "Old and new passphrases are the same. Database %s(%p) master secret did not change.",
457 common().dbName(), this);
460 if (mSecret
) { mSecret
.reset(); }
462 common().invalidateBlob(); // blob state changed
463 secdebug("KCdb", "Database %s(%p) master secret changed", common().dbName(), this);
464 encode(); // force rebuild of local blob
466 // send out a notification
467 notify(kNotificationEventPassphraseChanged
);
469 // I guess this counts as an activity
474 // Second stage of keychain synchronization: overwrite the original keychain's
475 // (this KeychainDatabase's) operational secrets
477 void KeychainDatabase::commitSecretsForSync(KeychainDatabase
&cloneDb
)
479 StLock
<Mutex
> _(common());
481 // try to detect spoofing
482 if (cloneDb
.mRecodingSource
!= this)
483 CssmError::throwMe(CSSM_ERRCODE_INVALID_DB_HANDLE
);
485 // in case we autolocked since starting the sync
486 makeUnlocked(); // call this because we already own the lock
487 cloneDb
.unlockDb(); // we may not own the lock here, so calling unlockDb will lock the cloneDb's common lock
489 // Decode all keys whose handles refer to this on-disk keychain so that
490 // if the holding client commits the key back to disk, it's encoded with
491 // the new operational secrets. The recoding client *must* hold a write
492 // lock for the on-disk keychain from the moment it starts recoding key
493 // items until after this call.
495 // @@@ This specific implementation is a workaround for 4003540.
496 std::vector
<U32HandleObject::Handle
> handleList
;
497 U32HandleObject::findAllRefs
<KeychainKey
>(handleList
);
498 size_t count
= handleList
.size();
500 for (unsigned int n
= 0; n
< count
; ++n
) {
501 RefPointer
<KeychainKey
> kckey
=
502 U32HandleObject::findRefAndLock
<KeychainKey
>(handleList
[n
], CSSMERR_CSP_INVALID_KEY_REFERENCE
);
503 StLock
<Mutex
> _(*kckey
/*, true*/);
504 if (kckey
->database().global().identifier() == identifier()) {
505 kckey
->key(); // force decode
506 kckey
->invalidateBlob();
507 secdebug("kcrecode", "changed extant key %p (proc %d)",
508 &*kckey
, kckey
->process().pid());
513 // it is now safe to replace the old op secrets
514 common().importSecrets(cloneDb
.common());
515 common().invalidateBlob();
520 // Extract the database master key as a proper Key object.
522 RefPointer
<Key
> KeychainDatabase::extractMasterKey(Database
&db
,
523 const AccessCredentials
*cred
, const AclEntryPrototype
*owner
,
524 uint32 usage
, uint32 attrs
)
526 // get and hold common lock
527 StLock
<Mutex
> _(common());
529 // force lock to require re-validation of credentials
532 // unlock to establish master secret
535 // extract the raw cryptographic key
536 CssmClient::WrapKey
wrap(Server::csp(), CSSM_ALGID_NONE
);
538 wrap(common().masterKey(), key
);
540 // make the key object and return it
541 return makeKey(db
, key
, attrs
& LocalKey::managedAttributes
, owner
);
546 // Unlock this database (if needed) by obtaining the master secret in some
547 // suitable way and then proceeding to unlock with it.
548 // Does absolutely nothing if the database is already unlocked.
549 // The makeUnlocked forms are identical except the assume the caller already
550 // holds the common lock.
552 void KeychainDatabase::unlockDb()
554 StLock
<Mutex
> _(common());
558 void KeychainDatabase::makeUnlocked()
560 return makeUnlocked(mCred
);
563 void KeychainDatabase::makeUnlocked(const AccessCredentials
*cred
)
566 secdebug("KCdb", "%p(%p) unlocking for makeUnlocked()", this, &common());
567 assert(mBlob
|| (mValidData
&& common().hasMaster()));
568 establishOldSecrets(cred
);
569 common().setUnlocked(); // mark unlocked
570 if (common().isLoginKeychain()) {
571 CssmKey master
= common().masterKey();
573 CssmClient::WrapKey
wrap(Server::csp(), CSSM_ALGID_NONE
);
574 wrap(master
, rawMaster
);
576 service_context_t context
= common().session().get_current_service_context();
577 service_client_stash_load_key(&context
, rawMaster
.keyData(), (int)rawMaster
.length());
579 } else if (common().isLoginKeychain()) {
581 service_context_t context
= common().session().get_current_service_context();
582 if ((service_client_kb_is_locked(&context
, &locked
, NULL
) == 0) && locked
) {
583 StSyncLock
<Mutex
, Mutex
> uisync(common().uiLock(), common());
584 QueryKeybagPassphrase
keybagQuery(common().session(), 3);
585 keybagQuery
.inferHints(Server::process());
586 if (keybagQuery
.query() != SecurityAgent::noReason
) {
587 syslog(LOG_NOTICE
, "failed to unlock iCloud keychain");
591 if (!mValidData
) { // need to decode to get our ACLs, master secret available
592 secdebug("KCdb", "%p(%p) is unlocked; decoding for makeUnlocked()", this, &common());
594 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
601 // Invoke the securityd_service to retrieve the keychain master
602 // key from the AppleFDEKeyStore.
604 void KeychainDatabase::stashDbCheck()
606 CssmAutoData
masterKey(Allocator::standard(Allocator::sensitive
));
607 CssmAutoData
encKey(Allocator::standard(Allocator::sensitive
));
611 void * stash_key
= NULL
;
612 int stash_key_len
= 0;
613 service_context_t context
= common().session().get_current_service_context();
614 rc
= service_client_stash_get_key(&context
, &stash_key
, &stash_key_len
);
617 masterKey
.copy(CssmData((void *)stash_key
,stash_key_len
));
618 memset(stash_key
, 0, stash_key_len
);
622 CssmError::throwMe(rc
);
626 StLock
<Mutex
> _(common());
628 // Now establish it as the keychain master key
629 CssmClient::Key
key(Server::csp(), masterKey
.get());
630 CssmKey::Header
&hdr
= key
.header();
631 hdr
.keyClass(CSSM_KEYCLASS_SESSION_KEY
);
632 hdr
.algorithm(CSSM_ALGID_3DES_3KEY_EDE
);
633 hdr
.usage(CSSM_KEYUSE_ANY
);
634 hdr
.blobType(CSSM_KEYBLOB_RAW
);
635 hdr
.blobFormat(CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING
);
636 common().setup(mBlob
, key
);
639 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
641 common().get_encryption_key(encKey
);
644 // when upgrading from pre-10.9 create a keybag if it doesn't exist with the encryption key
645 // only do this after we have verified the master key unlocks the login.keychain
646 if (service_client_kb_load(&context
) == KB_BagNotFound
) {
647 service_client_kb_create(&context
, encKey
.data(), (int)encKey
.length());
652 // Get the keychain master key and invoke the securityd_service
653 // to stash it in the AppleFDEKeyStore ready for commit to the
656 void KeychainDatabase::stashDb()
658 CssmAutoData
data(Allocator::standard(Allocator::sensitive
));
661 StLock
<Mutex
> _(common());
663 if (!common().isValid()) {
664 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
667 CssmKey key
= common().masterKey();
668 data
.copy(key
.keyData());
671 service_context_t context
= common().session().get_current_service_context();
672 int rc
= service_client_stash_set_key(&context
, data
.data(), (int)data
.length());
673 if (rc
!= 0) CssmError::throwMe(rc
);
677 // The following unlock given an explicit passphrase, rather than using
678 // (special cred sample based) default procedures.
680 void KeychainDatabase::unlockDb(const CssmData
&passphrase
)
682 StLock
<Mutex
> _(common());
683 makeUnlocked(passphrase
);
686 void KeychainDatabase::makeUnlocked(const CssmData
&passphrase
)
689 if (decode(passphrase
))
692 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
693 } else if (!mValidData
) { // need to decode to get our ACLs, passphrase available
695 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
698 if (common().isLoginKeychain()) {
700 service_context_t context
= common().session().get_current_service_context();
701 if (!common().session().keybagGetState(session_keybag_check_master_key
) || ((service_client_kb_is_locked(&context
, &locked
, NULL
) == 0) && locked
)) {
702 unlock_keybag(*this, passphrase
.data(), (int)passphrase
.length());
712 // Nonthrowing passphrase-based unlock. This returns false if unlock failed.
713 // Note that this requires an explicitly given passphrase.
714 // Caller must hold common lock.
716 bool KeychainDatabase::decode(const CssmData
&passphrase
)
719 common().setup(mBlob
, passphrase
);
720 bool success
= decode();
721 if (success
&& common().isLoginKeychain()) {
722 unlock_keybag(*this, passphrase
.data(), (int)passphrase
.length());
729 // Given the established master secret, decode the working keys and other
730 // functional secrets for this database. Return false (do NOT throw) if
731 // the decode fails. Call this in low(er) level code once you established
734 bool KeychainDatabase::decode()
737 assert(common().hasMaster());
738 void *privateAclBlob
;
739 if (common().unlockDb(mBlob
, &privateAclBlob
)) {
741 acl().importBlob(mBlob
->publicAclBlob(), privateAclBlob
);
744 Allocator::standard().free(privateAclBlob
);
747 secdebug("KCdb", "%p decode failed", this);
753 // Given an AccessCredentials for this database, wring out the existing primary
754 // database secret by whatever means necessary.
755 // On entry, caller must hold the database common lock. It will be held
756 // throughout except when user interaction is required. User interaction
757 // requires relinquishing the database common lock and taking the UI lock. On
758 // return from user interaction, the UI lock is relinquished and the database
759 // common lock must be reacquired. At no time may the caller hold both locks.
760 // On exit, the crypto core has its master secret. If things go wrong,
761 // we will throw a suitable exception. Note that encountering any malformed
762 // credential sample will throw, but this is not guaranteed -- don't assume
763 // that NOT throwing means creds is entirely well-formed (it may just be good
764 // enough to work THIS time).
767 // Walk through the creds. Fish out those credentials (in order) that
768 // are for unlock processing (they have no ACL subject correspondents),
769 // and (try to) obey each in turn, until one produces a valid secret
770 // or you run out. If no special samples are found at all, interpret that as
771 // "use the system global default," which happens to be hard-coded right here.
773 void KeychainDatabase::establishOldSecrets(const AccessCredentials
*creds
)
775 bool forSystem
= this->belongsToSystem(); // this keychain belongs to the system security domain
777 // attempt system-keychain unlock
779 SystemKeychainKey
systemKeychain(kSystemUnlockFile
);
780 if (systemKeychain
.matches(mBlob
->randomSignature
)) {
781 secdebug("KCdb", "%p attempting system unlock", this);
782 common().setup(mBlob
, CssmClient::Key(Server::csp(), systemKeychain
.key(), true));
788 list
<CssmSample
> samples
;
789 if (creds
&& creds
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
, samples
)) {
790 for (list
<CssmSample
>::iterator it
= samples
.begin(); it
!= samples
.end(); it
++) {
791 TypedList
&sample
= *it
;
792 sample
.checkProper();
793 switch (sample
.type()) {
794 // interactively prompt the user - no additional data
795 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
:
797 if (interactiveUnlock())
801 // try to use an explicitly given passphrase - Data:passphrase
802 case CSSM_SAMPLE_TYPE_PASSWORD
:
803 if (sample
.length() != 2)
804 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
805 secdebug("KCdb", "%p attempting passphrase unlock", this);
806 if (decode(sample
[1]))
809 // try to open with a given master key - Data:CSP or KeyHandle, Data:CssmKey
810 case CSSM_SAMPLE_TYPE_SYMMETRIC_KEY
:
811 case CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY
:
813 secdebug("KCdb", "%p attempting explicit key unlock", this);
814 common().setup(mBlob
, keyFromCreds(sample
, 4));
819 // explicitly defeat the default action but don't try anything in particular
820 case CSSM_WORDID_CANCELED
:
821 secdebug("KCdb", "%p defeat default action", this);
824 // Unknown sub-sample for unlocking.
825 // If we wanted to be fascist, we could now do
826 // CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
827 // But instead we try to be tolerant and continue on.
828 // This DOES however count as an explicit attempt at specifying unlock,
829 // so we will no longer try the default case below...
830 secdebug("KCdb", "%p unknown sub-sample unlock (%d) ignored", this, sample
.type());
839 if (interactiveUnlock())
844 // out of options - no secret obtained
845 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
848 bool KeychainDatabase::interactiveUnlock()
850 secdebug("KCdb", "%p attempting interactive unlock", this);
851 SecurityAgent::Reason reason
= SecurityAgent::noReason
;
852 QueryUnlock
query(*this);
853 // take UI interlock and release DbCommon lock (to avoid deadlocks)
854 StSyncLock
<Mutex
, Mutex
> uisync(common().uiLock(), common());
856 // now that we have the UI lock, interact unless another thread unlocked us first
858 query
.inferHints(Server::process());
860 if (mSaveSecret
&& reason
== SecurityAgent::noReason
) {
861 query
.retrievePassword(mSecret
);
865 secdebug("KCdb", "%p was unlocked during uiLock delay", this);
868 if (common().isLoginKeychain()) {
870 service_context_t context
= common().session().get_current_service_context();
871 if ((service_client_kb_is_locked(&context
, &locked
, NULL
) == 0) && locked
) {
872 QueryKeybagNewPassphrase
keybagQuery(common().session());
873 keybagQuery
.inferHints(Server::process());
874 CssmAutoData
pass(Allocator::standard(Allocator::sensitive
));
875 CssmAutoData
oldPass(Allocator::standard(Allocator::sensitive
));
876 SecurityAgent::Reason queryReason
= keybagQuery
.query(oldPass
, pass
);
877 if (queryReason
== SecurityAgent::noReason
) {
878 service_client_kb_change_secret(&context
, oldPass
.data(), (int)oldPass
.length(), pass
.data(), (int)pass
.length());
879 } else if (queryReason
== SecurityAgent::resettingPassword
) {
880 query
.retrievePassword(pass
);
881 service_client_kb_reset(&context
, pass
.data(), (int)pass
.length());
887 return reason
== SecurityAgent::noReason
;
892 // Same thing, but obtain a new secret somehow and set it into the common.
894 bool KeychainDatabase::establishNewSecrets(const AccessCredentials
*creds
, SecurityAgent::Reason reason
)
896 list
<CssmSample
> samples
;
897 if (creds
&& creds
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
, samples
)) {
898 for (list
<CssmSample
>::iterator it
= samples
.begin(); it
!= samples
.end(); it
++) {
899 TypedList
&sample
= *it
;
900 sample
.checkProper();
901 switch (sample
.type()) {
902 // interactively prompt the user
903 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
:
905 secdebug("KCdb", "%p specified interactive passphrase", this);
906 QueryNewPassphrase
query(*this, reason
);
907 StSyncLock
<Mutex
, Mutex
> uisync(common().uiLock(), common());
908 query
.inferHints(Server::process());
909 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
910 CssmAutoData
oldPassphrase(Allocator::standard(Allocator::sensitive
));
911 if (query(oldPassphrase
, passphrase
) == SecurityAgent::noReason
) {
912 common().setup(NULL
, passphrase
);
913 change_secret_on_keybag(*this, oldPassphrase
.data(), (int)oldPassphrase
.length(), passphrase
.data(), (int)passphrase
.length());
918 // try to use an explicitly given passphrase
919 case CSSM_SAMPLE_TYPE_PASSWORD
:
921 secdebug("KCdb", "%p specified explicit passphrase", this);
922 if (sample
.length() != 2)
923 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
924 if (common().isLoginKeychain()) {
925 CssmAutoData
oldPassphrase(Allocator::standard(Allocator::sensitive
));
926 list
<CssmSample
> oldSamples
;
927 creds
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
, oldSamples
);
928 for (list
<CssmSample
>::iterator oit
= oldSamples
.begin(); oit
!= oldSamples
.end(); oit
++) {
929 TypedList
&tmpList
= *oit
;
930 tmpList
.checkProper();
931 if (tmpList
.type() == CSSM_SAMPLE_TYPE_PASSWORD
) {
932 if (tmpList
.length() == 2) {
933 oldPassphrase
= tmpList
[1].data();
937 if (!oldPassphrase
.length() && mSecret
&& mSecret
.length()) {
938 oldPassphrase
= mSecret
;
940 if ((oldPassphrase
.length() == sample
[1].data().length()) &&
941 !memcmp(oldPassphrase
.data(), sample
[1].data().data(), oldPassphrase
.length()) &&
942 oldPassphrase
.length()) {
943 // don't change master key if the passwords are the same
946 common().setup(NULL
, sample
[1]);
947 change_secret_on_keybag(*this, oldPassphrase
.data(), (int)oldPassphrase
.length(), sample
[1].data().data(), (int)sample
[1].data().length());
950 common().setup(NULL
, sample
[1]);
954 // try to open with a given master key
955 case CSSM_WORDID_SYMMETRIC_KEY
:
956 case CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY
:
957 secdebug("KCdb", "%p specified explicit master key", this);
958 common().setup(NULL
, keyFromCreds(sample
, 3));
960 // explicitly defeat the default action but don't try anything in particular
961 case CSSM_WORDID_CANCELED
:
962 secdebug("KCdb", "%p defeat default action", this);
965 // Unknown sub-sample for acquiring new secret.
966 // If we wanted to be fascist, we could now do
967 // CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
968 // But instead we try to be tolerant and continue on.
969 // This DOES however count as an explicit attempt at specifying unlock,
970 // so we will no longer try the default case below...
971 secdebug("KCdb", "%p unknown sub-sample acquisition (%d) ignored",
972 this, sample
.type());
977 // default action -- interactive (only)
978 QueryNewPassphrase
query(*this, reason
);
979 StSyncLock
<Mutex
, Mutex
> uisync(common().uiLock(), common());
980 query
.inferHints(Server::process());
981 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
982 CssmAutoData
oldPassphrase(Allocator::standard(Allocator::sensitive
));
983 if (query(oldPassphrase
, passphrase
) == SecurityAgent::noReason
) {
984 common().setup(NULL
, passphrase
);
985 change_secret_on_keybag(*this, oldPassphrase
.data(), (int)oldPassphrase
.length(), passphrase
.data(), (int)passphrase
.length());
990 // out of options - no secret obtained
991 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
996 // Given a (truncated) Database credentials TypedList specifying a master key,
997 // locate the key and return a reference to it.
999 CssmClient::Key
KeychainDatabase::keyFromCreds(const TypedList
&sample
, unsigned int requiredLength
)
1001 // decode TypedList structure (sample type; Data:CSPHandle; Data:CSSM_KEY)
1002 assert(sample
.type() == CSSM_SAMPLE_TYPE_SYMMETRIC_KEY
|| sample
.type() == CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY
);
1003 if (sample
.length() != requiredLength
1004 || sample
[1].type() != CSSM_LIST_ELEMENT_DATUM
1005 || sample
[2].type() != CSSM_LIST_ELEMENT_DATUM
1006 || (requiredLength
== 4 && sample
[3].type() != CSSM_LIST_ELEMENT_DATUM
))
1007 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1008 KeyHandle
&handle
= *sample
[1].data().interpretedAs
<KeyHandle
>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1009 // We used to be able to check the length but supporting multiple client
1010 // architectures dishes that (sizeof(CSSM_KEY) varies due to alignment and
1011 // field-size differences). The decoding in the transition layer should
1012 // serve as a sufficient garbling check anyway.
1013 if (sample
[2].data().data() == NULL
)
1014 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1015 CssmKey
&key
= *sample
[2].data().interpretedAs
<CssmKey
>();
1017 if (key
.header().cspGuid() == gGuidAppleCSPDL
) {
1018 // handleOrKey is a SecurityServer KeyHandle; ignore key argument
1019 return safer_cast
<LocalKey
&>(*Server::key(handle
));
1021 if (sample
.type() == CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY
) {
1023 Contents (see DefaultCredentials::unlockKey in libsecurity_keychain/defaultcreds.cpp)
1025 sample[0] sample type
1026 sample[1] csp handle for master or wrapping key; is really a keyhandle
1027 sample[2] masterKey [not used since securityd cannot interpret; use sample[1] handle instead]
1028 sample[3] UnlockReferralRecord data, in this case the flattened symmetric key
1031 // RefPointer<Key> Server::key(KeyHandle key)
1032 KeyHandle keyhandle
= *sample
[1].data().interpretedAs
<KeyHandle
>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1033 CssmData
&flattenedKey
= sample
[3].data();
1034 RefPointer
<Key
> unwrappingKey
= Server::key(keyhandle
);
1035 Database
&db
=unwrappingKey
->database();
1037 CssmKey rawWrappedKey
;
1038 unflattenKey(flattenedKey
, rawWrappedKey
);
1040 RefPointer
<Key
> masterKey
;
1041 CssmData emptyDescriptiveData
;
1042 const AccessCredentials
*cred
= NULL
;
1043 const AclEntryPrototype
*owner
= NULL
;
1044 CSSM_KEYUSE usage
= CSSM_KEYUSE_ANY
;
1045 CSSM_KEYATTR_FLAGS attrs
= CSSM_KEYATTR_EXTRACTABLE
; //CSSM_KEYATTR_RETURN_REF |
1047 // Get default credentials for unwrappingKey (the one on the token)
1048 // Copied from Statics::Statics() in libsecurity_keychain/aclclient.cpp
1049 // Following KeyItem::getCredentials, one sees that the "operation" parameter
1050 // e.g. "CSSM_ACL_AUTHORIZATION_DECRYPT" is ignored
1051 Allocator
&alloc
= Allocator::standard();
1052 AutoCredentials
promptCred(alloc
, 3);// enable interactive prompting
1054 // promptCred: a credential permitting user prompt confirmations
1056 // a KEYCHAIN_PROMPT sample, both by itself and in a THRESHOLD
1057 // a PROMPTED_PASSWORD sample
1058 promptCred
.sample(0) = TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
);
1059 promptCred
.sample(1) = TypedList(alloc
, CSSM_SAMPLE_TYPE_THRESHOLD
,
1060 new(alloc
) ListElement(TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
)));
1061 promptCred
.sample(2) = TypedList(alloc
, CSSM_SAMPLE_TYPE_PROMPTED_PASSWORD
,
1062 new(alloc
) ListElement(alloc
, CssmData()));
1064 // This unwrap object is here just to provide a context
1065 CssmClient::UnwrapKey
unwrap(Server::csp(), CSSM_ALGID_NONE
); //ok to lie about csp here
1066 unwrap
.mode(CSSM_ALGMODE_NONE
);
1067 unwrap
.padding(CSSM_PADDING_PKCS1
);
1068 unwrap
.cred(promptCred
);
1069 unwrap
.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT
, uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7
));
1070 Security::Context
*tmpContext
;
1071 CSSM_CC_HANDLE CCHandle
= unwrap
.handle();
1072 /*CSSM_RETURN rx = */ CSSM_GetContext (CCHandle
, (CSSM_CONTEXT_PTR
*)&tmpContext
);
1074 // OK, this is skanky but necessary. We overwrite fields in the context struct
1076 tmpContext
->ContextType
= CSSM_ALGCLASS_ASYMMETRIC
;
1077 tmpContext
->AlgorithmType
= CSSM_ALGID_RSA
;
1079 db
.unwrapKey(*tmpContext
, cred
, owner
, unwrappingKey
, NULL
, usage
, attrs
,
1080 rawWrappedKey
, masterKey
, emptyDescriptiveData
);
1082 Allocator::standard().free(rawWrappedKey
.KeyData
.Data
);
1084 return safer_cast
<LocalKey
&>(*masterKey
).key();
1088 // not a KeyHandle reference; use key as a raw key
1089 if (key
.header().blobType() != CSSM_KEYBLOB_RAW
)
1090 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE
);
1091 if (key
.header().keyClass() != CSSM_KEYCLASS_SESSION_KEY
)
1092 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
1093 return CssmClient::Key(Server::csp(), key
, true);
1097 void unflattenKey(const CssmData
&flatKey
, CssmKey
&rawKey
)
1099 // unflatten the raw input key naively: key header then key data
1100 // We also convert it back to host byte order
1101 // A CSSM_KEY is a CSSM_KEYHEADER followed by a CSSM_DATA
1103 // Now copy: header, then key struct, then key data
1104 memcpy(&rawKey
.KeyHeader
, flatKey
.Data
, sizeof(CSSM_KEYHEADER
));
1105 memcpy(&rawKey
.KeyData
, flatKey
.Data
+ sizeof(CSSM_KEYHEADER
), sizeof(CSSM_DATA
));
1106 const uint32 keyDataLength
= flatKey
.length() - sizeof(CSSM_KEY
);
1107 rawKey
.KeyData
.Data
= Allocator::standard().malloc
<uint8
>(keyDataLength
);
1108 rawKey
.KeyData
.Length
= keyDataLength
;
1109 memcpy(rawKey
.KeyData
.Data
, flatKey
.Data
+ sizeof(CSSM_KEY
), keyDataLength
);
1110 Security::n2hi(rawKey
.KeyHeader
); // convert it to host byte order
1115 // Verify a putative database passphrase.
1116 // If the database is already unlocked, just check the passphrase.
1117 // Otherwise, unlock with that passphrase and report success.
1118 // Caller must hold the common lock.
1120 bool KeychainDatabase::validatePassphrase(const CssmData
&passphrase
) const
1122 if (common().hasMaster()) {
1123 // verify against known secret
1124 return common().validatePassphrase(passphrase
);
1126 // no master secret - perform "blind" unlock to avoid actual unlock
1128 DatabaseCryptoCore test
;
1129 test
.setup(mBlob
, passphrase
);
1130 test
.decodeCore(mBlob
, NULL
);
1140 // Lock this database
1142 void KeychainDatabase::lockDb()
1149 // Given a Key for this database, encode it into a blob and return it.
1151 KeyBlob
*KeychainDatabase::encodeKey(const CssmKey
&key
, const CssmData
&pubAcl
, const CssmData
&privAcl
)
1153 bool inTheClear
= false;
1154 if((key
.keyClass() == CSSM_KEYCLASS_PUBLIC_KEY
) &&
1155 !(key
.attribute(CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT
))) {
1158 StLock
<Mutex
> _(common());
1162 // tell the cryptocore to form the key blob
1163 return common().encodeKeyCore(key
, pubAcl
, privAcl
, inTheClear
);
1168 // Given a "blobbed" key for this database, decode it into its real
1169 // key object and (re)populate its ACL.
1171 void KeychainDatabase::decodeKey(KeyBlob
*blob
, CssmKey
&key
, void * &pubAcl
, void * &privAcl
)
1173 StLock
<Mutex
> _(common());
1175 if(!blob
->isClearText())
1176 makeUnlocked(); // we need our keys
1178 common().decodeKeyCore(blob
, key
, pubAcl
, privAcl
);
1179 // memory protocol: pubAcl points into blob; privAcl was allocated
1185 // Given a KeychainKey (that implicitly belongs to another keychain),
1186 // return it encoded using this keychain's operational secrets.
1188 KeyBlob
*KeychainDatabase::recodeKey(KeychainKey
&oldKey
)
1190 if (mRecodingSource
!= &oldKey
.referent
<KeychainDatabase
>()) {
1191 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
1193 oldKey
.instantiateAcl(); // make sure key is decoded
1194 CssmData publicAcl
, privateAcl
;
1195 oldKey
.exportBlob(publicAcl
, privateAcl
);
1196 // NB: blob's memory belongs to caller, not the common
1199 * Make sure the new key is in the same cleartext/encrypted state.
1201 bool inTheClear
= false;
1202 assert(oldKey
.blob());
1203 if(oldKey
.blob() && oldKey
.blob()->isClearText()) {
1207 KeyBlob
*blob
= common().encodeKeyCore(oldKey
.cssmKey(), publicAcl
, privateAcl
, inTheClear
);
1208 oldKey
.acl().allocator
.free(publicAcl
);
1209 oldKey
.acl().allocator
.free(privateAcl
);
1215 // Modify database parameters
1217 void KeychainDatabase::setParameters(const DBParameters
¶ms
)
1219 StLock
<Mutex
> _(common());
1221 common().mParams
= params
;
1222 common().invalidateBlob(); // invalidate old blobs
1223 activity(); // (also resets the timeout timer)
1224 secdebug("KCdb", "%p common %p(%s) set params=(%u,%u)",
1225 this, &common(), dbName(), params
.idleTimeout
, params
.lockOnSleep
);
1230 // Retrieve database parameters
1232 void KeychainDatabase::getParameters(DBParameters
¶ms
)
1234 StLock
<Mutex
> _(common());
1236 params
= common().mParams
;
1237 //activity(); // getting parameters does not reset the idle timer
1242 // RIGHT NOW, database ACLs are attached to the database.
1243 // This will soon move upstairs.
1245 SecurityServerAcl
&KeychainDatabase::acl()
1252 // Intercept ACL change requests and reset blob validity
1254 void KeychainDatabase::instantiateAcl()
1256 StLock
<Mutex
> _(common());
1260 void KeychainDatabase::changedAcl()
1262 StLock
<Mutex
> _(common());
1268 // Check an incoming DbBlob for basic viability
1270 void KeychainDatabase::validateBlob(const DbBlob
*blob
)
1272 // perform basic validation on the blob
1274 blob
->validate(CSSMERR_APPLEDL_INVALID_DATABASE_BLOB
);
1275 switch (blob
->version()) {
1276 #if defined(COMPAT_OSX_10_0)
1277 case DbBlob::version_MacOS_10_0
:
1280 case DbBlob::version_MacOS_10_1
:
1283 CssmError::throwMe(CSSMERR_APPLEDL_INCOMPATIBLE_DATABASE_BLOB
);
1289 // Debugging support
1291 #if defined(DEBUGDUMP)
1293 void KeychainDbCommon::dumpNode()
1295 PerSession::dumpNode();
1296 uint32 sig
; memcpy(&sig
, &mIdentifier
.signature(), sizeof(sig
));
1297 Debug::dump(" %s[%8.8x]", mIdentifier
.dbName(), sig
);
1299 Debug::dump(" locked");
1301 time_t whenTime
= time_t(when());
1302 Debug::dump(" unlocked(%24.24s/%.2g)", ctime(&whenTime
),
1303 (when() - Time::now()).seconds());
1305 Debug::dump(" params=(%u,%u)", mParams
.idleTimeout
, mParams
.lockOnSleep
);
1308 void KeychainDatabase::dumpNode()
1310 PerProcess::dumpNode();
1311 Debug::dump(" %s vers=%u",
1312 mValidData
? " data" : " nodata", version
);
1314 uint32 sig
; memcpy(&sig
, &mBlob
->randomSignature
, sizeof(sig
));
1315 Debug::dump(" blob=%p[%8.8x]", mBlob
, sig
);
1317 Debug::dump(" noblob");
1325 // DbCommon basic features
1327 KeychainDbCommon::KeychainDbCommon(Session
&ssn
, const DbIdentifier
&id
)
1328 : LocalDbCommon(ssn
), sequence(0), version(1), mIdentifier(id
),
1329 mIsLocked(true), mValidParams(false), mLoginKeychain(false)
1331 // match existing DbGlobal or create a new one
1333 Server
&server
= Server::active();
1334 StLock
<Mutex
> _(server
);
1335 if (KeychainDbGlobal
*dbglobal
=
1336 server
.findFirst
<KeychainDbGlobal
, const DbIdentifier
&>(&KeychainDbGlobal::identifier
, identifier())) {
1338 secdebug("KCdb", "%p linking to existing DbGlobal %p", this, dbglobal
);
1340 // DbGlobal not present; make a new one
1341 parent(*new KeychainDbGlobal(identifier()));
1342 secdebug("KCdb", "%p linking to new DbGlobal %p", this, &global());
1345 // link lifetime to the Session
1346 session().addReference(*this);
1348 if (strcasestr(id
.dbName(), "login.keychain") != NULL
) {
1349 mLoginKeychain
= true;
1353 if (mLoginKeychain
&& !session().keybagGetState(session_keybag_loaded
)) {
1354 service_context_t context
= session().get_current_service_context();
1355 if (service_client_kb_load(&context
) == 0) {
1356 session().keybagSetState(session_keybag_loaded
);
1361 KeychainDbCommon::~KeychainDbCommon()
1363 SECURITYD_KEYCHAIN_RELEASE(this, (char*)this->dbName());
1365 // explicitly unschedule ourselves
1366 Server::active().clearTimer(this);
1367 if (mLoginKeychain
) {
1368 session().keybagClearState(session_keybag_unlocked
);
1372 KeychainDbGlobal
&KeychainDbCommon::global() const
1374 return parent
<KeychainDbGlobal
>();
1378 void KeychainDbCommon::select()
1381 void KeychainDbCommon::unselect()
1386 void KeychainDbCommon::makeNewSecrets()
1388 // we already have a master key (right?)
1389 assert(hasMaster());
1391 // tell crypto core to generate the use keys
1392 DatabaseCryptoCore::generateNewSecrets();
1394 // we're now officially "unlocked"; set the timer
1400 // All unlocking activity ultimately funnels through this method.
1401 // This unlocks a DbCommon using the secrets setup in its crypto core
1402 // component, and performs all the housekeeping needed to represent
1403 // the state change.
1404 // Returns true if unlock was successful, false if it failed due to
1405 // invalid/insufficient secrets. Throws on other errors.
1407 bool KeychainDbCommon::unlockDb(DbBlob
*blob
, void **privateAclBlob
)
1410 // Tell the cryptocore to (try to) decode itself. This will fail
1411 // in an astonishing variety of ways if the passphrase is wrong.
1412 assert(hasMaster());
1413 decodeCore(blob
, privateAclBlob
);
1414 secdebug("KCdb", "%p unlock successful", this);
1416 secdebug("KCdb", "%p unlock failed", this);
1420 // get the database parameters only if we haven't got them yet
1421 if (!mValidParams
) {
1422 mParams
= blob
->params
;
1423 n2hi(mParams
.idleTimeout
);
1424 mValidParams
= true; // sticky
1427 bool isLocked
= mIsLocked
;
1429 setUnlocked(); // mark unlocked
1432 // broadcast unlock notification, but only if we were previously locked
1433 notify(kNotificationEventUnlocked
);
1434 SECURITYD_KEYCHAIN_UNLOCK(this, (char*)this->dbName());
1439 void KeychainDbCommon::setUnlocked()
1441 session().addReference(*this); // active/held
1442 mIsLocked
= false; // mark unlocked
1443 activity(); // set timeout timer
1447 void KeychainDbCommon::lockDb()
1451 StLock
<Mutex
> _(*this);
1453 DatabaseCryptoCore::invalidate();
1454 notify(kNotificationEventLocked
);
1455 SECURITYD_KEYCHAIN_LOCK(this, (char*)this->dbName());
1456 Server::active().clearTimer(this);
1458 mIsLocked
= true; // mark locked
1461 // this call may destroy us if we have no databases anymore
1462 session().removeReference(*this);
1466 if (mLoginKeychain
&& lock
) {
1467 // Locking can happens on a timer outside of a process request session().get_current_service_context()
1468 service_context_t context
= { session().sessionId(), session().originatorUid(), {} };
1469 service_client_kb_lock(&context
);
1470 session().keybagClearState(session_keybag_unlocked
);
1475 DbBlob
*KeychainDbCommon::encode(KeychainDatabase
&db
)
1477 assert(!isLocked()); // must have been unlocked by caller
1479 // export database ACL to blob form
1480 CssmData pubAcl
, privAcl
;
1481 db
.acl().exportBlob(pubAcl
, privAcl
);
1483 // tell the cryptocore to form the blob
1485 form
.randomSignature
= identifier();
1486 form
.sequence
= sequence
;
1487 form
.params
= mParams
;
1488 h2ni(form
.params
.idleTimeout
);
1490 assert(hasMaster());
1491 DbBlob
*blob
= encodeCore(form
, pubAcl
, privAcl
);
1494 db
.acl().allocator
.free(pubAcl
);
1495 db
.acl().allocator
.free(privAcl
);
1501 // Perform deferred lock processing for a database.
1503 void KeychainDbCommon::action()
1505 secdebug("KCdb", "common %s(%p) locked by timer", dbName(), this);
1509 void KeychainDbCommon::activity()
1512 secdebug("KCdb", "setting DbCommon %p timer to %d",
1513 this, int(mParams
.idleTimeout
));
1514 Server::active().setTimer(this, Time::Interval(int(mParams
.idleTimeout
)));
1518 void KeychainDbCommon::sleepProcessing()
1520 secdebug("KCdb", "common %s(%p) sleep-lock processing", dbName(), this);
1521 StLock
<Mutex
> _(*this);
1522 if (mParams
.lockOnSleep
)
1526 void KeychainDbCommon::lockProcessing()
1533 // We consider a keychain to belong to the system domain if it resides
1534 // in /Library/Keychains. That's not exactly fool-proof, but we don't
1535 // currently have any internal markers to interrogate.
1537 bool KeychainDbCommon::belongsToSystem() const
1539 if (const char *name
= this->dbName())
1540 return !strncmp(name
, "/Library/Keychains/", 19);
1546 // Keychain global objects
1548 KeychainDbGlobal::KeychainDbGlobal(const DbIdentifier
&id
)
1553 KeychainDbGlobal::~KeychainDbGlobal()
1555 secdebug("KCdb", "DbGlobal %p destroyed", this);