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.
41 #define __STDC_WANT_LIB_EXT1__ 1
44 #include "kcdatabase.h"
45 #include "agentquery.h"
49 #include "notifications.h"
50 #include "SecRandom.h"
51 #include "keychainstasherinterface.h"
52 #include <vector> // @@@ 4003540 workaround
53 #include <security_cdsa_utilities/acl_any.h> // for default owner ACLs
54 #include <security_cdsa_utilities/cssmendian.h>
55 #include <security_cdsa_client/wrapkey.h>
56 #include <security_cdsa_client/genkey.h>
57 #include <security_cdsa_client/signclient.h>
58 #include <security_cdsa_client/cryptoclient.h>
59 #include <security_cdsa_client/macclient.h>
60 #include <securityd_client/dictionary.h>
61 #include <security_utilities/endian.h>
62 #include <security_utilities/errors.h>
63 #include "securityd_service/securityd_service/securityd_service_client.h"
64 #include <AssertMacros.h>
66 #include <sys/sysctl.h>
67 #include <sys/kauth.h>
70 #include <corecrypto/ccmode_siv.h>
73 void unflattenKey(const CssmData
&flatKey
, CssmKey
&rawKey
); //>> make static method on KeychainDatabase
78 KeychainDbCommon::CommonSet
KeychainDbCommon::mCommonSet
;
79 ReadWriteLock
KeychainDbCommon::mRWCommonLock
;
81 // Process is using a cached effective uid, login window switches uid after the intial connection
82 static void get_process_euid(pid_t pid
, uid_t
* out_euid
)
84 if (!out_euid
) return;
86 struct kinfo_proc proc_info
= {};
87 int mib
[] = {CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, pid
};
88 size_t len
= sizeof(struct kinfo_proc
);
90 ret
= sysctl(mib
, (sizeof(mib
)/sizeof(int)), &proc_info
, &len
, NULL
, 0);
93 if ((ret
== 0) && (proc_info
.kp_eproc
.e_ucred
.cr_uid
!= 0)) {
94 *out_euid
= proc_info
.kp_eproc
.e_ucred
.cr_uid
;
99 unlock_keybag(KeychainDatabase
& db
, const void * secret
, int secret_len
)
103 if (!db
.common().isLoginKeychain()) return 0;
105 service_context_t context
= db
.common().session().get_current_service_context();
107 // login window attempts to change the password before a session has a uid
108 if (context
.s_uid
== AU_DEFAUDITID
) {
109 get_process_euid(db
.process().pid(), &context
.s_uid
);
112 // try to unlock first if not found then load/create or unlock
113 // loading should happen when the kb common object is created
114 // if it doesn't exist yet then the unlock will fail and we'll create everything
115 rc
= service_client_kb_unlock(&context
, secret
, secret_len
);
116 if (rc
== KB_BagNotLoaded
) {
117 if (service_client_kb_load(&context
) == KB_BagNotFound
) {
118 rc
= service_client_kb_create(&context
, secret
, secret_len
);
120 rc
= service_client_kb_unlock(&context
, secret
, secret_len
);
124 if (rc
!= 0) { // if we just upgraded make sure we swap the encryption key to the password
125 if (!db
.common().session().keybagGetState(session_keybag_check_master_key
)) {
126 CssmAutoData
encKey(Allocator::standard(Allocator::sensitive
));
127 db
.common().get_encryption_key(encKey
);
128 if ((rc
= service_client_kb_unlock(&context
, encKey
.data(), (int)encKey
.length())) == 0) {
129 rc
= service_client_kb_change_secret(&context
, encKey
.data(), (int)encKey
.length(), secret
, secret_len
);
132 if (rc
!= 0) { // if a login.keychain password exists but doesnt on the keybag update it
134 if ((secret_len
> 0) && service_client_kb_is_locked(&context
, NULL
, &no_pin
) == 0) {
136 syslog(LOG_ERR
, "Updating iCloud keychain passphrase for uid %d", context
.s_uid
);
137 service_client_kb_change_secret(&context
, NULL
, 0, secret
, secret_len
);
141 } // session_keybag_check_master_key
145 db
.common().session().keybagSetState(session_keybag_unlocked
|session_keybag_loaded
|session_keybag_check_master_key
);
147 syslog(LOG_ERR
, "Failed to unlock iCloud keychain for uid %d", context
.s_uid
);
154 change_secret_on_keybag(KeychainDatabase
& db
, const void * secret
, int secret_len
, const void * new_secret
, int new_secret_len
)
156 if (!db
.common().isLoginKeychain()) return;
158 service_context_t context
= db
.common().session().get_current_service_context();
160 // login window attempts to change the password before a session has a uid
161 if (context
.s_uid
== AU_DEFAUDITID
) {
162 get_process_euid(db
.process().pid(), &context
.s_uid
);
165 // if a login.keychain doesn't exist yet it comes into securityd as a create then change_secret
166 // we need to create the keybag in this case if it doesn't exist
167 int rc
= service_client_kb_change_secret(&context
, secret
, secret_len
, new_secret
, new_secret_len
);
168 if (rc
== KB_BagNotLoaded
) {
169 if (service_client_kb_load(&context
) == KB_BagNotFound
) {
170 rc
= service_client_kb_create(&context
, new_secret
, new_secret_len
);
172 rc
= service_client_kb_change_secret(&context
, secret
, secret_len
, new_secret
, new_secret_len
);
176 // this makes it possible to restore a deleted keybag on condition it still exists in kernel
177 if (rc
!= KB_Success
) {
178 service_client_kb_save(&context
);
181 // if for some reason we are locked lets unlock so later we don't try and throw up SecurityAgent dialog
183 if ((service_client_kb_is_locked(&context
, &locked
, NULL
) == KB_Success
) && locked
) {
184 rc
= service_client_kb_unlock(&context
, new_secret
, new_secret_len
);
185 if (rc
!= KB_Success
) {
186 syslog(LOG_ERR
, "Failed to unlock iCloud keychain for uid %d (%d)", context
.s_uid
, (int)rc
);
191 // Attempt to unlock the keybag with a AccessCredentials password.
192 // Honors UI disabled flags from clients set in the cred before prompt.
194 unlock_keybag_with_cred(KeychainDatabase
&db
, const AccessCredentials
*cred
){
195 list
<CssmSample
> samples
;
196 if (cred
&& cred
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
, samples
)) {
197 for (list
<CssmSample
>::iterator it
= samples
.begin(); it
!= samples
.end(); it
++) {
198 TypedList
&sample
= *it
;
199 sample
.checkProper();
200 switch (sample
.type()) {
201 // interactively prompt the user - no additional data
202 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
:
205 Okay, this is messy. We need to hold the common lock to be certain we're modifying the world
206 as we intend. But UI ^ common, and QueryKeybagPassphrase::query() has tons of side effects by necessity,
207 so just confirm that the operation did what we wanted after the fact.
209 bool query_success
= false;
210 bool unlock_success
= false;
214 StSyncLock
<Mutex
, Mutex
> uisync(db
.common().uiLock(), db
.common());
215 // Once we get the ui lock, check whether another thread has already unlocked keybag
217 query_success
= false;
218 service_context_t context
= db
.common().session().get_current_service_context();
219 if ((service_client_kb_is_locked(&context
, &locked
, NULL
) == 0) && locked
) {
220 QueryKeybagPassphrase
keybagQuery(db
.common().session(), 3);
221 keybagQuery
.inferHints(Server::process());
222 if (keybagQuery
.query() == SecurityAgent::noReason
) {
223 query_success
= true;
226 // another thread already unlocked the keybag
227 query_success
= true; // NOT unlock_success because we have the wrong lock
229 } // StSyncLock goes out of scope, we have common lock again
231 service_context_t context
= db
.common().session().get_current_service_context();
232 if ((service_client_kb_is_locked(&context
, &locked
, NULL
) == 0) && !locked
) {
233 unlock_success
= true;
236 secnotice("KCdb", "Unlocking the keybag again (threading?)");
239 } while (query_success
&& !unlock_success
);
242 // try to use an explicitly given passphrase - Data:passphrase
243 case CSSM_SAMPLE_TYPE_PASSWORD
: {
244 if (sample
.length() != 2)
245 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
246 secinfo("KCdb", "attempting passphrase unlock of keybag");
247 if (unlock_keybag(db
, sample
[1].data().data(), (int)sample
[1].data().length())) {
253 // Unknown sub-sample for unlocking.
254 secinfo("KCdb", "keybag: unknown sub-sample unlock (%d) ignored", sample
.type());
264 // Create a Database object from initial parameters (create operation)
266 KeychainDatabase::KeychainDatabase(const DLDbIdentifier
&id
, const DBParameters
¶ms
, Process
&proc
,
267 const AccessCredentials
*cred
, const AclEntryPrototype
*owner
)
268 : LocalDatabase(proc
), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive
)), mSaveSecret(false), version(0), mBlob(NULL
), mRecoded(false)
270 // save a copy of the credentials for later access control
271 mCred
= DataWalkers::copy(cred
, Allocator::standard());
273 // create a new random signature to complete the DLDbIdentifier
274 DbBlob::Signature newSig
;
276 (MacOSError::check
)(SecRandomCopyBytes(kSecRandomDefault
, sizeof(newSig
.bytes
), newSig
.bytes
));
277 DbIdentifier
ident(id
, newSig
);
279 // create common block and initialize
280 // Since this is a creation step, figure out the correct blob version for this database
281 RefPointer
<KeychainDbCommon
> newCommon
= new KeychainDbCommon(proc
.session(), ident
, CommonBlob::getCurrentVersionForDb(ident
.dbName()));
282 newCommon
->initializeKeybag();
284 StLock
<Mutex
> _(*newCommon
);
287 // new common is now visible (in ident-map) but we hold its lock
289 // establish the new master secret
290 establishNewSecrets(cred
, SecurityAgent::newDatabase
, false);
292 // set initial database parameters
293 common().mParams
= params
;
295 // the common is "unlocked" now
296 common().makeNewSecrets();
298 // establish initial ACL
300 acl().cssmSetInitial(*owner
);
302 acl().cssmSetInitial(new AnyAclSubject());
305 // for now, create the blob immediately
308 proc
.addReference(*this);
310 // this new keychain is unlocked; make it so
313 secinfo("KCdb", "creating keychain %p %s with common %p", this, (char*)this->dbName(), &common());
318 // Create a Database object from a database blob (decoding)
320 KeychainDatabase::KeychainDatabase(const DLDbIdentifier
&id
, const DbBlob
*blob
, Process
&proc
,
321 const AccessCredentials
*cred
)
322 : LocalDatabase(proc
), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive
)), mSaveSecret(false), version(0), mBlob(NULL
), mRecoded(false)
326 // save a copy of the credentials for later access control
327 mCred
= DataWalkers::copy(cred
, Allocator::standard());
328 mBlob
= blob
->copy();
330 // check to see if we already know about this database
331 DbIdentifier
ident(id
, blob
->randomSignature
);
332 Session
&session
= process().session();
333 RefPointer
<KeychainDbCommon
> com
;
334 secinfo("kccommon", "looking for a common at %s", ident
.dbName());
335 if (KeychainDbCommon::find(ident
, session
, com
)) {
337 secinfo("KCdb", "joining keychain %p %s with common %p", this, (char*)this->dbName(), &common());
339 // DbCommon not present; make a new one
340 secinfo("kccommon", "no common found");
342 common().mParams
= blob
->params
;
343 secinfo("KCdb", "making keychain %p %s with common %p", this, (char*)this->dbName(), &common());
344 // this DbCommon is locked; no timer or reference setting
346 proc
.addReference(*this);
349 void KeychainDbCommon::insert()
351 StReadWriteLock
_(mRWCommonLock
, StReadWriteLock::Write
);
355 void KeychainDbCommon::insertHoldingLock()
357 mCommonSet
.insert(this);
362 // find or make a DbCommon. Returns true if an existing one was found and used.
363 bool KeychainDbCommon::find(const DbIdentifier
&ident
, Session
&session
, RefPointer
<KeychainDbCommon
> &common
, uint32 requestedVersion
, KeychainDbCommon
* cloneFrom
)
365 // Prepare to drop the mRWCommonLock.
367 StReadWriteLock
_(mRWCommonLock
, StReadWriteLock::Read
);
368 for (CommonSet::const_iterator it
= mCommonSet
.begin(); it
!= mCommonSet
.end(); ++it
) {
369 if (&session
== &(*it
)->session() && ident
== (*it
)->identifier()) {
371 secinfo("kccommon", "found a common for %s at %p", ident
.dbName(), common
.get());
377 // not found. Grab the write lock, ensure that nobody has beaten us to adding,
378 // and then create a DbCommon and add it to the map.
380 StReadWriteLock
_(mRWCommonLock
, StReadWriteLock::Write
);
381 for (CommonSet::const_iterator it
= mCommonSet
.begin(); it
!= mCommonSet
.end(); ++it
) {
382 if (&session
== &(*it
)->session() && ident
== (*it
)->identifier()) {
384 secinfo("kccommon", "found a common for %s at %p", ident
.dbName(), common
.get());
391 common
= new KeychainDbCommon(session
, ident
, *cloneFrom
);
392 } else if(requestedVersion
!= CommonBlob::version_none
) {
393 common
= new KeychainDbCommon(session
, ident
, requestedVersion
);
395 common
= new KeychainDbCommon(session
, ident
);
398 secinfo("kccommon", "made a new common for %s at %p", ident
.dbName(), common
.get());
400 // Can't call insert() here, because it grabs the write lock (which we have).
401 common
->insertHoldingLock();
403 common
->initializeKeybag();
409 // Special-purpose constructor for keychain synchronization. Copies an
410 // existing keychain but uses the operational keys from secretsBlob. The
411 // new KeychainDatabase will silently replace the existing KeychainDatabase
412 // as soon as the client declares that re-encoding of all keychain items is
413 // finished. This is a little perilous since it allows a client to dictate
414 // securityd state, but we try to ensure that only the client that started
415 // the re-encoding can declare it done.
417 KeychainDatabase::KeychainDatabase(KeychainDatabase
&src
, Process
&proc
, DbHandle dbToClone
)
418 : LocalDatabase(proc
), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive
)), mSaveSecret(false), version(0), mBlob(NULL
), mRecoded(false)
420 mCred
= DataWalkers::copy(src
.mCred
, Allocator::standard());
422 // Give this KeychainDatabase a temporary name
423 std::string newDbName
= std::string("////") + std::string(src
.identifier().dbName());
424 DLDbIdentifier
newDLDbIdent(src
.identifier().dlDbIdentifier().ssuid(), newDbName
.c_str(), src
.identifier().dlDbIdentifier().dbLocation());
425 DbIdentifier
ident(newDLDbIdent
, src
.identifier());
427 // create common block and initialize
428 RefPointer
<KeychainDbCommon
> newCommon
= new KeychainDbCommon(proc
.session(), ident
);
429 newCommon
->initializeKeybag();
430 StLock
<Mutex
> _(*newCommon
);
434 // set initial database parameters from the source keychain
435 common().mParams
= src
.common().mParams
;
437 // establish the source keychain's master secret as ours
438 // @@@ NB: this is a v. 0.1 assumption. We *should* trigger new UI
439 // that offers the user the option of using the existing password
440 // or choosing a new one. That would require a new
441 // SecurityAgentQuery type, new UI, and--possibly--modifications to
442 // ensure that the new password is available here to generate the
443 // new master secret.
444 src
.unlockDb(false); // precaution for masterKey()
445 common().setup(src
.blob(), src
.common().masterKey());
447 // import the operational secrets
448 RefPointer
<KeychainDatabase
> srcKC
= Server::keychain(dbToClone
);
449 common().importSecrets(srcKC
->common());
451 // import source keychain's ACL
452 CssmData pubAcl
, privAcl
;
453 src
.acl().exportBlob(pubAcl
, privAcl
);
454 importBlob(pubAcl
.data(), privAcl
.data());
455 src
.acl().allocator
.free(pubAcl
);
456 src
.acl().allocator
.free(privAcl
);
458 // indicate that this keychain should be allowed to do some otherwise
459 // risky things required for copying, like re-encoding keys
460 mRecodingSource
= &src
;
462 common().setUnlocked();
467 proc
.addReference(*this);
468 secinfo("SSdb", "database %s(%p) created as copy, common at %p",
469 common().dbName(), this, &common());
472 // Make a new KeychainDatabase from an old one, but have a completely different location
473 KeychainDatabase::KeychainDatabase(const DLDbIdentifier
& id
, KeychainDatabase
&src
, Process
&proc
)
474 : LocalDatabase(proc
), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive
)), mSaveSecret(false), version(0), mBlob(NULL
), mRecoded(false)
476 mCred
= DataWalkers::copy(src
.mCred
, Allocator::standard());
478 DbIdentifier
ident(id
, src
.identifier());
480 // create common block and initialize
481 RefPointer
<KeychainDbCommon
> newCommon
;
482 if(KeychainDbCommon::find(ident
, process().session(), newCommon
, CommonBlob::version_none
, &src
.common())) {
483 // A common already existed. Write over it, but note that everything may go horribly from here on out.
484 secinfo("kccommon", "Found common where we didn't expect. Possible strange behavior ahead.");
485 newCommon
->cloneFrom(src
.common());
488 StLock
<Mutex
> _(*newCommon
);
491 // set initial database parameters from the source keychain
492 common().mParams
= src
.common().mParams
;
494 // import source keychain's ACL
495 CssmData pubAcl
, privAcl
;
496 src
.acl().exportBlob(pubAcl
, privAcl
);
497 importBlob(pubAcl
.data(), privAcl
.data());
498 src
.acl().allocator
.free(pubAcl
);
499 src
.acl().allocator
.free(privAcl
);
501 // Copy the source database's blob, if possible
503 mBlob
= src
.mBlob
->copy();
504 version
= src
.version
;
507 // We've copied everything we can from our source. If they were valid, so are we.
508 mValidData
= src
.mValidData
;
510 proc
.addReference(*this);
511 secinfo("SSdb", "database %s(%p) created as expected clone, common at %p", common().dbName(), this, &common());
515 // Make a new KeychainDatabase from an old one, but have entirely new operational secrets
516 KeychainDatabase::KeychainDatabase(uint32 requestedVersion
, KeychainDatabase
&src
, Process
&proc
)
517 : LocalDatabase(proc
), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive
)), mSaveSecret(false), version(0), mBlob(NULL
), mRecoded(false)
519 mCred
= DataWalkers::copy(src
.mCred
, Allocator::standard());
521 // Give this KeychainDatabase a temporary name
522 // this must canonicalize to a different path than the original DB, otherwise another process opening the existing DB wil find this new KeychainDbCommon
523 // and call decodeCore with the old blob, overwriting the new secrets and wreaking havoc
524 std::string newDbName
= std::string("////") + std::string(src
.identifier().dbName()) + std::string("_com.apple.security.keychain.migrating");
525 DLDbIdentifier
newDLDbIdent(src
.identifier().dlDbIdentifier().ssuid(), newDbName
.c_str(), src
.identifier().dlDbIdentifier().dbLocation());
526 DbIdentifier
ident(newDLDbIdent
, src
.identifier());
528 // hold the lock for src's common during this operation (to match locking common locking order with KeychainDatabase::recodeKey)
529 StLock
<Mutex
> __(src
.common());
531 // create common block and initialize
532 RefPointer
<KeychainDbCommon
> newCommon
;
533 if(KeychainDbCommon::find(ident
, process().session(), newCommon
, requestedVersion
)) {
534 // A common already existed here. Write over it, but note that everything may go horribly from here on out.
535 secinfo("kccommon", "Found common where we didn't expect. Possible strange behavior ahead.");
536 newCommon
->cloneFrom(src
.common(), requestedVersion
);
538 newCommon
->initializeKeybag();
539 StLock
<Mutex
> _(*newCommon
);
542 // We want to re-use the master secrets from the source database (and so the
543 // same password), but reroll new operational secrets.
545 // Copy the master secret over...
546 src
.unlockDb(false); // precaution
548 common().setup(src
.blob(), src
.common().masterKey(), false); // keep the new common's version intact
550 // set initial database parameters from the source keychain
551 common().mParams
= src
.common().mParams
;
553 // and make new operational secrets
554 common().makeNewSecrets();
556 // import source keychain's ACL
557 CssmData pubAcl
, privAcl
;
558 src
.acl().exportBlob(pubAcl
, privAcl
);
559 importBlob(pubAcl
.data(), privAcl
.data());
560 src
.acl().allocator
.free(pubAcl
);
561 src
.acl().allocator
.free(privAcl
);
563 // indicate that this keychain should be allowed to do some otherwise
564 // risky things required for copying, like re-encoding keys
565 mRecodingSource
= &src
;
567 common().setUnlocked();
572 proc
.addReference(*this);
573 secinfo("SSdb", "database %s(%p) created as expected copy, common at %p",
574 common().dbName(), this, &common());
578 // Destroy a Database
580 KeychainDatabase::~KeychainDatabase()
582 secinfo("KCdb", "deleting database %s(%p) common %p",
583 common().dbName(), this, &common());
584 Allocator::standard().free(mCred
);
585 Allocator::standard().free(mBlob
);
590 // Basic Database virtual implementations
592 KeychainDbCommon
&KeychainDatabase::common() const
594 return parent
<KeychainDbCommon
>();
597 const char *KeychainDatabase::dbName() const
599 return common().dbName();
602 bool KeychainDatabase::transient() const
604 return false; // has permanent store
607 AclKind
KeychainDatabase::aclKind() const
612 Database
*KeychainDatabase::relatedDatabase()
618 // (Re-)Authenticate the database. This changes the stored credentials.
620 void KeychainDatabase::authenticate(CSSM_DB_ACCESS_TYPE mode
,
621 const AccessCredentials
*cred
)
623 StLock
<Mutex
> _(common());
625 // the (Apple specific) RESET bit means "lock the database now"
627 case CSSM_DB_ACCESS_RESET
:
628 secinfo("KCdb", "%p ACCESS_RESET triggers keychain lock", this);
632 // store the new credentials for future use
633 secinfo("KCdb", "%p authenticate stores new database credentials", this);
634 AccessCredentials
*newCred
= DataWalkers::copy(cred
, Allocator::standard());
635 Allocator::standard().free(mCred
);
642 // Make a new KeychainKey.
643 // If PERMANENT is off, make a temporary key instead.
644 // The db argument allows you to create for another KeychainDatabase (only);
645 // it defaults to ourselves.
647 RefPointer
<Key
> KeychainDatabase::makeKey(Database
&db
, const CssmKey
&newKey
,
648 uint32 moreAttributes
, const AclEntryPrototype
*owner
)
650 StLock
<Mutex
> lock(common());
651 if (moreAttributes
& CSSM_KEYATTR_PERMANENT
)
652 return new KeychainKey(db
, newKey
, moreAttributes
, owner
);
654 return process().makeTemporaryKey(newKey
, moreAttributes
, owner
);
657 RefPointer
<Key
> KeychainDatabase::makeKey(const CssmKey
&newKey
,
658 uint32 moreAttributes
, const AclEntryPrototype
*owner
)
660 return makeKey(*this, newKey
, moreAttributes
, owner
);
665 // Return the database blob, recalculating it as needed.
667 DbBlob
*KeychainDatabase::blob()
669 StLock
<Mutex
> _(common());
671 makeUnlocked(false); // unlock to get master secret
672 encode(); // (re)encode blob if needed
674 activity(); // reset timeout
675 assert(validBlob()); // better have a valid blob now...
681 // Encode the current database as a blob.
682 // Note that this returns memory we own and keep.
683 // Caller must hold common lock.
685 void KeychainDatabase::encode()
687 DbBlob
*blob
= common().encode(*this);
688 Allocator::standard().free(mBlob
);
690 version
= common().version
;
691 secinfo("KCdb", "encoded database %p common %p(%s) version %u params=(%u,%u)",
692 this, &common(), dbName(), version
,
693 common().mParams
.idleTimeout
, common().mParams
.lockOnSleep
);
697 // Change the passphrase on a database
699 void KeychainDatabase::changePassphrase(const AccessCredentials
*cred
)
701 // get and hold the common lock (don't let other threads break in here)
702 StLock
<Mutex
> _(common());
704 list
<CssmSample
> samples
;
705 bool hasOldSecret
= cred
&& cred
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
, samples
);
706 samples
.clear(); // Can't count the samples at the end because I could specify CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK twice
707 bool hasNewSecret
= cred
&& cred
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
, samples
);
709 // If no creds we do interactive, if both we do silently. If unequal, think harder.
710 if (hasOldSecret
!= hasNewSecret
) {
711 if (!hasOldSecret
&& !isLocked()) {
712 // Alternative: do a confirm_access SA dialog and run validatePassphrase on it (what client name?)
713 // That's risky for now, but it would cover the remaining use case.
714 secerror("KCdb: changePassphrase credential set has no old secret and KC not locked");
715 MacOSError::throwMe(errSecAuthFailed
);
719 secnotice("KCdb", "changePassphrase proceeding with old %i, new %i, locked %i", hasOldSecret
, hasNewSecret
, isLocked());
721 if (hasOldSecret
&& !checkCredentials(cred
)) {
722 secinfo("KCdb", "Cannot change passphrase for (%s): existing passphrase does not match", common().dbName());
723 MacOSError::throwMe(errSecAuthFailed
);
726 // establish OLD secret - i.e. unlock the database. You'll be prompted if !hasOldSecrets and DB is locked.
727 if (common().isLoginKeychain()) mSaveSecret
= true;
728 makeUnlocked(cred
, false);
730 // establish NEW secret
731 if(!establishNewSecrets(cred
, SecurityAgent::changePassphrase
, true)) {
732 secinfo("KCdb", "Old and new passphrases are the same. Database %s(%p) master secret did not change.",
733 common().dbName(), this);
736 if (mSecret
) { mSecret
.reset(); }
738 common().invalidateBlob(); // blob state changed
739 secinfo("KCdb", "Database %s(%p) master secret changed", common().dbName(), this);
740 encode(); // force rebuild of local blob
742 // send out a notification
743 notify(kNotificationEventPassphraseChanged
);
745 // I guess this counts as an activity
750 // Second stage of keychain synchronization: overwrite the original keychain's
751 // (this KeychainDatabase's) operational secrets
753 void KeychainDatabase::commitSecretsForSync(KeychainDatabase
&cloneDb
)
755 StLock
<Mutex
> _(common());
757 // try to detect spoofing
758 if (cloneDb
.mRecodingSource
!= this)
759 CssmError::throwMe(CSSM_ERRCODE_INVALID_DB_HANDLE
);
761 // in case we autolocked since starting the sync
762 makeUnlocked(false); // call this because we already own the lock
763 cloneDb
.unlockDb(false); // we may not own the lock here, so calling unlockDb will lock the cloneDb's common lock
765 // Decode all keys whose handles refer to this on-disk keychain so that
766 // if the holding client commits the key back to disk, it's encoded with
767 // the new operational secrets. The recoding client *must* hold a write
768 // lock for the on-disk keychain from the moment it starts recoding key
769 // items until after this call.
771 // @@@ This specific implementation is a workaround for 4003540.
772 std::vector
<U32HandleObject::Handle
> handleList
;
773 U32HandleObject::findAllRefs
<KeychainKey
>(handleList
);
774 size_t count
= handleList
.size();
776 for (unsigned int n
= 0; n
< count
; ++n
) {
777 RefPointer
<KeychainKey
> kckey
=
778 U32HandleObject::findRefAndLock
<KeychainKey
>(handleList
[n
], CSSMERR_CSP_INVALID_KEY_REFERENCE
);
779 StLock
<Mutex
> _(*kckey
/*, true*/);
780 if (kckey
->database().global().identifier() == identifier()) {
781 kckey
->key(); // force decode
782 kckey
->invalidateBlob();
783 secinfo("kcrecode", "changed extant key %p (proc %d)",
784 &*kckey
, kckey
->process().pid());
789 // mark down that we just recoded
792 // it is now safe to replace the old op secrets
793 common().importSecrets(cloneDb
.common());
794 common().invalidateBlob();
799 // Extract the database master key as a proper Key object.
801 RefPointer
<Key
> KeychainDatabase::extractMasterKey(Database
&db
,
802 const AccessCredentials
*cred
, const AclEntryPrototype
*owner
,
803 uint32 usage
, uint32 attrs
)
805 // get and hold common lock
806 StLock
<Mutex
> _(common());
808 // force lock to require re-validation of credentials
811 // unlock to establish master secret
814 // extract the raw cryptographic key
815 CssmClient::WrapKey
wrap(Server::csp(), CSSM_ALGID_NONE
);
817 wrap(common().masterKey(), key
);
819 // make the key object and return it
820 return makeKey(db
, key
, attrs
& LocalKey::managedAttributes
, owner
);
825 // Unlock this database (if needed) by obtaining the master secret in some
826 // suitable way and then proceeding to unlock with it.
827 // Does absolutely nothing if the database is already unlocked.
828 // The makeUnlocked forms are identical except the assume the caller already
829 // holds the common lock.
831 void KeychainDatabase::unlockDb(bool unlockKeybag
)
833 StLock
<Mutex
> _(common());
834 makeUnlocked(unlockKeybag
);
837 void KeychainDatabase::makeUnlocked(bool unlockKeybag
)
839 return makeUnlocked(mCred
, unlockKeybag
);
842 void KeychainDatabase::makeUnlocked(const AccessCredentials
*cred
, bool unlockKeybag
)
845 secnotice("KCdb", "%p(%p) unlocking for makeUnlocked()", this, &common());
846 assert(mBlob
|| (mValidData
&& common().hasMaster()));
847 bool asking_again
= false;
850 secnotice("KCdb", "makeUnlocked: establishing old secrets again (threading?)");
852 establishOldSecrets(cred
);
854 } while (!common().hasMaster());
855 common().setUnlocked(); // mark unlocked
856 if (common().isLoginKeychain()) {
857 CssmKey master
= common().masterKey();
859 CssmClient::WrapKey
wrap(Server::csp(), CSSM_ALGID_NONE
);
860 wrap(master
, rawMaster
);
862 service_context_t context
= common().session().get_current_service_context();
863 service_client_stash_load_key(&context
, rawMaster
.keyData(), (int)rawMaster
.length());
865 } else if (unlockKeybag
&& common().isLoginKeychain()) {
867 service_context_t context
= common().session().get_current_service_context();
868 if ((service_client_kb_is_locked(&context
, &locked
, NULL
) == 0) && locked
) {
869 if (!unlock_keybag_with_cred(*this, cred
)) {
870 syslog(LOG_NOTICE
, "failed to unlock iCloud keychain");
874 if (!mValidData
) { // need to decode to get our ACLs, master secret available
875 secnotice("KCdb", "%p(%p) is unlocked; decoding for makeUnlocked()", this, &common());
877 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
884 Invoke securityd_service to load the keybag and retrieve the masterkey.
885 Also load masterkey from KeychainStasher and compare to make sure new stash works properly
887 void KeychainDatabase::stashDbCheck()
889 secnotice("KCdb", "Loading stashed key");
890 CssmAutoData
masterKey(Allocator::standard(Allocator::sensitive
));
891 CssmAutoData
encKey(Allocator::standard(Allocator::sensitive
));
893 // We're going to double-load during transition
896 service_context_t context
= common().session().get_current_service_context();
897 // SIDE EFFECT: loads the user's keybag
898 int servicerc
= service_client_stash_get_key(&context
, &s_key
, &s_keylen
);
899 if (servicerc
!= KB_Success
) {
900 secerror("KCdb: failed to load stash from securityd_service: %d", servicerc
);
902 secnotice("KCdb", "securityd_service claims get_key success");
907 OSStatus agentrc
= loadKeyFromStashAgent(common().session().originatorUid(), &a_key
, &a_keylen
);
908 if (agentrc
!= errSecSuccess
) {
909 secerror("KCdb: failed to load stash from KeychainStasher: %d", (int)agentrc
);
911 secnotice("KCdb", "KeychainStasher claims loadKey success");
916 if (servicerc
!= KB_Success
&& agentrc
!= errSecSuccess
) {
917 __security_simulatecrash(CFSTR("Both old and new stashes failed to load"), __sec_exception_code_BadStash
);
918 CssmError::throwMe(servicerc
); // For now
919 } else if (servicerc
== KB_Success
&& agentrc
== errSecSuccess
) {
920 if (s_keylen
!= a_keylen
|| a_keylen
== 0) {
921 __security_simulatecrash(CFSTR("Stashed key lengths disagree or are zero"), __sec_exception_code_BadStash
);
922 } else if (cc_cmp_safe(a_keylen
, a_key
, s_key
) != 0) {
923 __security_simulatecrash(CFSTR("Keybytes disagree"), __sec_exception_code_BadStash
);
928 memset_s(s_key
, s_keylen
, 0, s_keylen
);
930 } else if (servicerc
== KB_Success
) {
933 } else if (agentrc
== errSecSuccess
) {
938 masterKey
.copy(CssmData(key
, keylen
));
939 memset_s(key
, keylen
, 0, keylen
);
942 secnotice("KCdb", "Retrieved stashed key, will establish");
944 StLock
<Mutex
> _(common());
946 // Now establish it as the keychain master key
947 CssmClient::Key
key(Server::csp(), masterKey
.get());
948 CssmKey::Header
&hdr
= key
.header();
949 hdr
.keyClass(CSSM_KEYCLASS_SESSION_KEY
);
950 hdr
.algorithm(CSSM_ALGID_3DES_3KEY_EDE
);
951 hdr
.usage(CSSM_KEYUSE_ANY
);
952 hdr
.blobType(CSSM_KEYBLOB_RAW
);
953 hdr
.blobFormat(CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING
);
954 common().setup(mBlob
, key
);
957 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
960 common().get_encryption_key(encKey
);
963 // when upgrading from pre-10.9 create a keybag if it doesn't exist with the encryption key
964 // only do this after we have verified the master key unlocks the login.keychain
965 if (service_client_kb_load(&context
) == KB_BagNotFound
) {
966 service_client_kb_create(&context
, encKey
.data(), (int)encKey
.length());
971 // Get the keychain master key and invoke the securityd_service
972 // to stash it in the AppleFDEKeyStore ready for commit to the
975 void KeychainDatabase::stashDb()
977 secnotice("KCdb", "Let's stash a key");
978 CssmAutoData
data(Allocator::standard(Allocator::sensitive
));
981 StLock
<Mutex
> _(common());
982 if (!common().isValid()) {
983 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
986 CssmKey key
= common().masterKey();
987 data
.copy(key
.keyData());
990 // We're going to double-stash during transition
991 service_context_t context
= common().session().get_current_service_context();
992 int servicerc
= service_client_stash_set_key(&context
, data
.data(), (int)data
.length());
993 if (servicerc
!= KB_Success
) {
994 secerror("KCdb: securityd_service stash failed: %d", servicerc
);
996 secnotice("KCdb", "securityd_service claims successful stash");
999 OSStatus agentrc
= stashKeyWithStashAgent(common().session().originatorUid(), data
.data(), data
.length());
1000 if (agentrc
!= errSecSuccess
) {
1001 secerror("KCdb: KeychainStasher stash failed: %d", (int)agentrc
);
1003 secnotice("KCdb", "KeychainStasher claims successful stash");
1006 if (servicerc
!= KB_Success
&& agentrc
!= errSecSuccess
) {
1007 __security_simulatecrash(CFSTR("Both old and new stash mechanisms failed"), __sec_exception_code_BadStash
);
1008 CssmError::throwMe(servicerc
); // For now
1010 secnotice("KCdb", "Key stashed");
1014 // The following unlock given an explicit passphrase, rather than using
1015 // (special cred sample based) default procedures.
1017 void KeychainDatabase::unlockDb(const CssmData
&passphrase
, bool unlockKeybag
)
1019 StLock
<Mutex
> _(common());
1020 makeUnlocked(passphrase
, unlockKeybag
);
1023 void KeychainDatabase::makeUnlocked(const CssmData
&passphrase
, bool unlockKeybag
)
1026 if (decode(passphrase
))
1029 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
1030 } else if (!mValidData
) { // need to decode to get our ACLs, passphrase available
1032 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
1035 if (unlockKeybag
&& common().isLoginKeychain()) {
1036 bool locked
= false;
1037 service_context_t context
= common().session().get_current_service_context();
1038 if (!common().session().keybagGetState(session_keybag_check_master_key
) || ((service_client_kb_is_locked(&context
, &locked
, NULL
) == 0) && locked
)) {
1039 unlock_keybag(*this, passphrase
.data(), (int)passphrase
.length());
1043 assert(!isLocked());
1049 // Nonthrowing passphrase-based unlock. This returns false if unlock failed.
1050 // Note that this requires an explicitly given passphrase.
1051 // Caller must hold common lock.
1053 bool KeychainDatabase::decode(const CssmData
&passphrase
)
1056 common().setup(mBlob
, passphrase
);
1057 bool success
= decode();
1058 if (success
&& common().isLoginKeychain()) {
1059 unlock_keybag(*this, passphrase
.data(), (int)passphrase
.length());
1066 // Given the established master secret, decode the working keys and other
1067 // functional secrets for this database. Return false (do NOT throw) if
1068 // the decode fails. Call this in low(er) level code once you established
1071 bool KeychainDatabase::decode()
1074 assert(common().hasMaster());
1075 void *privateAclBlob
;
1076 if (common().unlockDb(mBlob
, &privateAclBlob
)) {
1078 acl().importBlob(mBlob
->publicAclBlob(), privateAclBlob
);
1081 Allocator::standard().free(privateAclBlob
);
1084 secinfo("KCdb", "%p decode failed", this);
1090 // Given an AccessCredentials for this database, wring out the existing primary
1091 // database secret by whatever means necessary.
1092 // On entry, caller must hold the database common lock. It will be held
1093 // throughout except when user interaction is required. User interaction
1094 // requires relinquishing the database common lock and taking the UI lock. On
1095 // return from user interaction, the UI lock is relinquished and the database
1096 // common lock must be reacquired. At no time may the caller hold both locks.
1097 // On exit, the crypto core has its master secret. If things go wrong,
1098 // we will throw a suitable exception. Note that encountering any malformed
1099 // credential sample will throw, but this is not guaranteed -- don't assume
1100 // that NOT throwing means creds is entirely well-formed (it may just be good
1101 // enough to work THIS time).
1104 // Walk through the creds. Fish out those credentials (in order) that
1105 // are for unlock processing (they have no ACL subject correspondents),
1106 // and (try to) obey each in turn, until one produces a valid secret
1107 // or you run out. If no special samples are found at all, interpret that as
1108 // "use the system global default," which happens to be hard-coded right here.
1110 void KeychainDatabase::establishOldSecrets(const AccessCredentials
*creds
)
1112 bool forSystem
= this->belongsToSystem(); // this keychain belongs to the system security domain
1114 // attempt system-keychain unlock
1116 SystemKeychainKey
systemKeychain(kSystemUnlockFile
);
1117 if (systemKeychain
.matches(mBlob
->randomSignature
)) {
1118 secinfo("KCdb", "%p attempting system unlock", this);
1119 common().setup(mBlob
, CssmClient::Key(Server::csp(), systemKeychain
.key(), true));
1125 list
<CssmSample
> samples
;
1126 if (creds
&& creds
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
, samples
)) {
1127 for (list
<CssmSample
>::iterator it
= samples
.begin(); it
!= samples
.end(); it
++) {
1128 TypedList
&sample
= *it
;
1129 sample
.checkProper();
1130 switch (sample
.type()) {
1131 // interactively prompt the user - no additional data
1132 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
:
1134 if (interactiveUnlock())
1138 // try to use an explicitly given passphrase - Data:passphrase
1139 case CSSM_SAMPLE_TYPE_PASSWORD
:
1140 if (sample
.length() != 2)
1141 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1142 secinfo("KCdb", "%p attempting passphrase unlock", this);
1143 if (decode(sample
[1]))
1146 // try to open with a given master key - Data:CSP or KeyHandle, Data:CssmKey
1147 case CSSM_SAMPLE_TYPE_SYMMETRIC_KEY
:
1148 case CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY
:
1150 secinfo("KCdb", "%p attempting explicit key unlock", this);
1151 common().setup(mBlob
, keyFromCreds(sample
, 4));
1156 case CSSM_SAMPLE_TYPE_KEYBAG_KEY
:
1158 secinfo("KCdb", "%p attempting keybag key unlock", this);
1159 common().setup(mBlob
, keyFromKeybag(sample
));
1164 // explicitly defeat the default action but don't try anything in particular
1165 case CSSM_WORDID_CANCELED
:
1166 secinfo("KCdb", "%p defeat default action", this);
1169 // Unknown sub-sample for unlocking.
1170 // If we wanted to be fascist, we could now do
1171 // CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
1172 // But instead we try to be tolerant and continue on.
1173 // This DOES however count as an explicit attempt at specifying unlock,
1174 // so we will no longer try the default case below...
1175 secinfo("KCdb", "%p unknown sub-sample unlock (%d) ignored", this, sample
.type());
1184 if (interactiveUnlock())
1189 // out of options - no secret obtained
1190 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
1194 // This function is almost identical to establishOldSecrets, but:
1195 // 1. It will never prompt the user; these credentials either work or they don't
1196 // 2. It will not change the secrets of this database
1198 // TODO: These two functions should probably be refactored to something nicer.
1199 bool KeychainDatabase::checkCredentials(const AccessCredentials
*creds
) {
1201 list
<CssmSample
> samples
;
1202 if (creds
&& creds
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
, samples
)) {
1203 for (list
<CssmSample
>::iterator it
= samples
.begin(); it
!= samples
.end(); it
++) {
1204 TypedList
&sample
= *it
;
1205 sample
.checkProper();
1206 switch (sample
.type()) {
1207 // interactively prompt the user - no additional data
1208 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
:
1209 // do nothing, because this function will never prompt the user
1210 secinfo("integrity", "%p ignoring keychain prompt", this);
1212 // try to use an explicitly given passphrase - Data:passphrase
1213 case CSSM_SAMPLE_TYPE_PASSWORD
:
1214 if (sample
.length() != 2)
1215 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1216 secinfo("integrity", "%p checking passphrase", this);
1217 if(validatePassphrase(sample
[1])) {
1221 // try to open with a given master key - Data:CSP or KeyHandle, Data:CssmKey
1222 case CSSM_SAMPLE_TYPE_SYMMETRIC_KEY
:
1223 case CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY
:
1225 secinfo("integrity", "%p attempting explicit key unlock", this);
1227 CssmClient::Key checkKey
= keyFromCreds(sample
, 4);
1228 if(common().validateKey(checkKey
)) {
1232 // ignore all problems in keyFromCreds
1233 secinfo("integrity", "%p caught error", this);
1240 // out of options - credentials don't match
1244 uint32_t KeychainDatabase::interactiveUnlockAttempts
= 0;
1246 // This does UI so needs the UI lock. It also interacts with the common, so needs the common lock. But can't have both at once!
1247 // Try to hold the UI lock for the smallest amount of time possible while having the common lock where needed.
1248 bool KeychainDatabase::interactiveUnlock()
1250 secinfo("KCdb", "%p attempting interactive unlock", this);
1251 interactiveUnlockAttempts
++;
1253 SecurityAgent::Reason reason
= SecurityAgent::noReason
;
1254 QueryUnlock
query(*this);
1257 query
.inferHints(Server::process());
1258 StSyncLock
<Mutex
, Mutex
> uisync(common().uiLock(), common());
1261 if (mSaveSecret
&& reason
== SecurityAgent::noReason
) {
1262 query
.retrievePassword(mSecret
);
1266 secinfo("KCdb", "%p was unlocked during uiLock delay", this);
1269 if (common().isLoginKeychain()) {
1270 bool locked
= false;
1271 service_context_t context
= common().session().get_current_service_context();
1272 if ((service_client_kb_is_locked(&context
, &locked
, NULL
) == 0) && locked
) {
1273 QueryKeybagNewPassphrase
keybagQuery(common().session());
1274 keybagQuery
.inferHints(Server::process());
1275 CssmAutoData
pass(Allocator::standard(Allocator::sensitive
));
1276 CssmAutoData
oldPass(Allocator::standard(Allocator::sensitive
));
1277 StSyncLock
<Mutex
, Mutex
> uisync(common().uiLock(), common());
1278 SecurityAgent::Reason queryReason
= keybagQuery
.query(oldPass
, pass
);
1280 if (queryReason
== SecurityAgent::noReason
) {
1281 service_client_kb_change_secret(&context
, oldPass
.data(), (int)oldPass
.length(), pass
.data(), (int)pass
.length());
1282 } else if (queryReason
== SecurityAgent::resettingPassword
) {
1283 query
.retrievePassword(pass
);
1284 service_client_kb_reset(&context
, pass
.data(), (int)pass
.length());
1290 return reason
== SecurityAgent::noReason
;
1293 uint32_t KeychainDatabase::getInteractiveUnlockAttempts() {
1294 if (csr_check(CSR_ALLOW_APPLE_INTERNAL
)) {
1295 // Not an internal install; don't answer
1298 return interactiveUnlockAttempts
;
1304 // Same thing, but obtain a new secret somehow and set it into the common.
1306 bool KeychainDatabase::establishNewSecrets(const AccessCredentials
*creds
, SecurityAgent::Reason reason
, bool change
)
1308 list
<CssmSample
> samples
;
1309 if (creds
&& creds
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
, samples
)) {
1310 for (list
<CssmSample
>::iterator it
= samples
.begin(); it
!= samples
.end(); it
++) {
1311 TypedList
&sample
= *it
;
1312 sample
.checkProper();
1313 switch (sample
.type()) {
1314 // interactively prompt the user
1315 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
:
1317 secinfo("KCdb", "%p specified interactive passphrase", this);
1318 QueryNewPassphrase
query(*this, reason
);
1319 StSyncLock
<Mutex
, Mutex
> uisync(common().uiLock(), common());
1320 query
.inferHints(Server::process());
1321 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
1322 CssmAutoData
oldPassphrase(Allocator::standard(Allocator::sensitive
));
1323 SecurityAgent::Reason
reason(query(oldPassphrase
, passphrase
));
1325 if (reason
== SecurityAgent::noReason
) {
1326 common().setup(NULL
, passphrase
);
1327 change_secret_on_keybag(*this, oldPassphrase
.data(), (int)oldPassphrase
.length(), passphrase
.data(), (int)passphrase
.length());
1332 // try to use an explicitly given passphrase
1333 case CSSM_SAMPLE_TYPE_PASSWORD
:
1335 secinfo("KCdb", "%p specified explicit passphrase", this);
1336 if (sample
.length() != 2)
1337 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1338 if (common().isLoginKeychain()) {
1339 CssmAutoData
oldPassphrase(Allocator::standard(Allocator::sensitive
));
1340 list
<CssmSample
> oldSamples
;
1341 creds
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
, oldSamples
);
1342 for (list
<CssmSample
>::iterator oit
= oldSamples
.begin(); oit
!= oldSamples
.end(); oit
++) {
1343 TypedList
&tmpList
= *oit
;
1344 tmpList
.checkProper();
1345 if (tmpList
.type() == CSSM_SAMPLE_TYPE_PASSWORD
) {
1346 if (tmpList
.length() == 2) {
1347 oldPassphrase
= tmpList
[1].data();
1351 if (!oldPassphrase
.length() && mSecret
&& mSecret
.length()) {
1352 oldPassphrase
= mSecret
;
1354 if ((oldPassphrase
.length() == sample
[1].data().length()) &&
1355 !memcmp(oldPassphrase
.data(), sample
[1].data().data(), oldPassphrase
.length()) &&
1356 oldPassphrase
.length()) {
1357 // don't change master key if the passwords are the same
1360 common().setup(NULL
, sample
[1]);
1361 change_secret_on_keybag(*this, oldPassphrase
.data(), (int)oldPassphrase
.length(), sample
[1].data().data(), (int)sample
[1].data().length());
1364 common().setup(NULL
, sample
[1]);
1368 // try to open with a given master key
1369 case CSSM_WORDID_SYMMETRIC_KEY
:
1370 case CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY
:
1371 secinfo("KCdb", "%p specified explicit master key", this);
1372 common().setup(NULL
, keyFromCreds(sample
, 3));
1374 // explicitly defeat the default action but don't try anything in particular
1375 case CSSM_WORDID_CANCELED
:
1376 secinfo("KCdb", "%p defeat default action", this);
1379 // Unknown sub-sample for acquiring new secret.
1380 // If we wanted to be fascist, we could now do
1381 // CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
1382 // But instead we try to be tolerant and continue on.
1383 // This DOES however count as an explicit attempt at specifying unlock,
1384 // so we will no longer try the default case below...
1385 secinfo("KCdb", "%p unknown sub-sample acquisition (%d) ignored",
1386 this, sample
.type());
1391 // default action -- interactive (only)
1392 QueryNewPassphrase
query(*this, reason
);
1393 StSyncLock
<Mutex
, Mutex
> uisync(common().uiLock(), common());
1394 query
.inferHints(Server::process());
1395 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
1396 CssmAutoData
oldPassphrase(Allocator::standard(Allocator::sensitive
));
1397 SecurityAgent::Reason
reason(query(oldPassphrase
, passphrase
));
1399 if (reason
== SecurityAgent::noReason
) {
1400 // If the DB was already unlocked then we have not yet checked the caller knows the old passphrase. Do so here.
1401 if (change
&& !validatePassphrase(oldPassphrase
.get())) {
1402 MacOSError::throwMe(errSecAuthFailed
);
1404 common().setup(NULL
, passphrase
);
1405 change_secret_on_keybag(*this, oldPassphrase
.data(), (int)oldPassphrase
.length(), passphrase
.data(), (int)passphrase
.length());
1410 // out of options - no secret obtained
1411 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
1416 // Given a (truncated) Database credentials TypedList specifying a master key,
1417 // locate the key and return a reference to it.
1419 CssmClient::Key
KeychainDatabase::keyFromCreds(const TypedList
&sample
, unsigned int requiredLength
)
1421 // decode TypedList structure (sample type; Data:CSPHandle; Data:CSSM_KEY)
1422 assert(sample
.type() == CSSM_SAMPLE_TYPE_SYMMETRIC_KEY
|| sample
.type() == CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY
);
1423 if (sample
.length() != requiredLength
1424 || sample
[1].type() != CSSM_LIST_ELEMENT_DATUM
1425 || sample
[2].type() != CSSM_LIST_ELEMENT_DATUM
1426 || (requiredLength
== 4 && sample
[3].type() != CSSM_LIST_ELEMENT_DATUM
))
1427 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1428 KeyHandle
&handle
= *sample
[1].data().interpretedAs
<KeyHandle
>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1429 // We used to be able to check the length but supporting multiple client
1430 // architectures dishes that (sizeof(CSSM_KEY) varies due to alignment and
1431 // field-size differences). The decoding in the transition layer should
1432 // serve as a sufficient garbling check anyway.
1433 if (sample
[2].data().data() == NULL
)
1434 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1435 CssmKey
&key
= *sample
[2].data().interpretedAs
<CssmKey
>();
1437 if (key
.header().cspGuid() == gGuidAppleCSPDL
) {
1438 // handleOrKey is a SecurityServer KeyHandle; ignore key argument
1439 return safer_cast
<LocalKey
&>(*Server::key(handle
));
1441 if (sample
.type() == CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY
) {
1443 Contents (see DefaultCredentials::unlockKey in libsecurity_keychain/defaultcreds.cpp)
1445 sample[0] sample type
1446 sample[1] csp handle for master or wrapping key; is really a keyhandle
1447 sample[2] masterKey [not used since securityd cannot interpret; use sample[1] handle instead]
1448 sample[3] UnlockReferralRecord data, in this case the flattened symmetric key
1451 // RefPointer<Key> Server::key(KeyHandle key)
1452 KeyHandle keyhandle
= *sample
[1].data().interpretedAs
<KeyHandle
>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1453 CssmData
&flattenedKey
= sample
[3].data();
1454 RefPointer
<Key
> unwrappingKey
= Server::key(keyhandle
);
1455 Database
&db
=unwrappingKey
->database();
1457 CssmKey rawWrappedKey
;
1458 unflattenKey(flattenedKey
, rawWrappedKey
);
1460 RefPointer
<Key
> masterKey
;
1461 CssmData emptyDescriptiveData
;
1462 const AccessCredentials
*cred
= NULL
;
1463 const AclEntryPrototype
*owner
= NULL
;
1464 CSSM_KEYUSE usage
= CSSM_KEYUSE_ANY
;
1465 CSSM_KEYATTR_FLAGS attrs
= CSSM_KEYATTR_EXTRACTABLE
; //CSSM_KEYATTR_RETURN_REF |
1467 // Get default credentials for unwrappingKey (the one on the token)
1468 // Copied from Statics::Statics() in libsecurity_keychain/aclclient.cpp
1469 // Following KeyItem::getCredentials, one sees that the "operation" parameter
1470 // e.g. "CSSM_ACL_AUTHORIZATION_DECRYPT" is ignored
1471 Allocator
&alloc
= Allocator::standard();
1472 AutoCredentials
promptCred(alloc
, 3);// enable interactive prompting
1474 // promptCred: a credential permitting user prompt confirmations
1476 // a KEYCHAIN_PROMPT sample, both by itself and in a THRESHOLD
1477 // a PROMPTED_PASSWORD sample
1478 promptCred
.sample(0) = TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
);
1479 promptCred
.sample(1) = TypedList(alloc
, CSSM_SAMPLE_TYPE_THRESHOLD
,
1480 new(alloc
) ListElement(TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
)));
1481 promptCred
.sample(2) = TypedList(alloc
, CSSM_SAMPLE_TYPE_PROMPTED_PASSWORD
,
1482 new(alloc
) ListElement(alloc
, CssmData()));
1484 // This unwrap object is here just to provide a context
1485 CssmClient::UnwrapKey
unwrap(Server::csp(), CSSM_ALGID_NONE
); //ok to lie about csp here
1486 unwrap
.mode(CSSM_ALGMODE_NONE
);
1487 unwrap
.padding(CSSM_PADDING_PKCS1
);
1488 unwrap
.cred(promptCred
);
1489 unwrap
.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT
, uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7
));
1490 Security::Context
*tmpContext
;
1491 CSSM_CC_HANDLE CCHandle
= unwrap
.handle();
1492 /*CSSM_RETURN rx = */ CSSM_GetContext (CCHandle
, (CSSM_CONTEXT_PTR
*)&tmpContext
);
1494 // OK, this is skanky but necessary. We overwrite fields in the context struct
1496 tmpContext
->ContextType
= CSSM_ALGCLASS_ASYMMETRIC
;
1497 tmpContext
->AlgorithmType
= CSSM_ALGID_RSA
;
1499 db
.unwrapKey(*tmpContext
, cred
, owner
, unwrappingKey
, NULL
, usage
, attrs
,
1500 rawWrappedKey
, masterKey
, emptyDescriptiveData
);
1502 Allocator::standard().free(rawWrappedKey
.KeyData
.Data
);
1504 return safer_cast
<LocalKey
&>(*masterKey
).key();
1506 else if (sample
.type() == CSSM_SAMPLE_TYPE_SYMMETRIC_KEY
&& sample
.length() == 4 && sample
[3].data().length() > 0) {
1508 Contents (see MasterKeyUnlockCredentials in libsecurity_cdsa_client/lib/aclclient.cpp)
1510 sample[0] sample type
1511 sample[1] 0, since we don't have a valid handle
1512 sample[2] CssmKey of the masterKey [can't immediately use since it includes a CSSM_DATA struct with pointers]
1513 sample[3] flattened symmetric master key, including the key data
1516 // Fix up key to include actual data
1517 CssmData
&flattenedKey
= sample
[3].data();
1518 unflattenKey(flattenedKey
, key
);
1520 // Check that we have a reasonable key
1521 if (key
.header().blobType() != CSSM_KEYBLOB_RAW
) {
1522 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE
);
1524 if (key
.header().keyClass() != CSSM_KEYCLASS_SESSION_KEY
) {
1525 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
1528 // bring the key into the CSP and return it
1529 return CssmClient::Key(Server::csp(), key
, true);
1531 // not a KeyHandle reference; use key as a raw key
1532 if (key
.header().blobType() != CSSM_KEYBLOB_RAW
)
1533 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE
);
1534 if (key
.header().keyClass() != CSSM_KEYCLASS_SESSION_KEY
)
1535 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
1536 return CssmClient::Key(Server::csp(), key
, true);
1540 void unflattenKey(const CssmData
&flatKey
, CssmKey
&rawKey
)
1542 // The format we're expecting is a CSSM_KEY followed by the actual key data:
1543 // CSSM_KEY : KEY DATA
1544 // which is approximately:
1545 // h2ni(CSSM_KEYHEADER) : 4 bytes padding : CSSM_DATA{?:?} : KEY BYTES
1547 // Note that CSSM_KEY includes a CSSM_DATA struct, which we will ignore as it has pointers.
1548 // The pointer and length will be set to whatever key data follows the CSSM_KEY in rawKey.
1550 // unflatten the raw input key naively: key header then key data
1551 // We also convert it back to host byte order
1552 // A CSSM_KEY is a CSSM_KEYHEADER followed by a CSSM_DATA
1554 // Now copy: header, then key struct, then key data
1555 memcpy(&rawKey
.KeyHeader
, flatKey
.Data
, sizeof(CSSM_KEYHEADER
));
1556 memcpy(&rawKey
.KeyData
, flatKey
.Data
+ sizeof(CSSM_KEYHEADER
), sizeof(CSSM_DATA
));
1557 size_t keyDataLength
= flatKey
.length() - sizeof(CSSM_KEY
);
1558 rawKey
.KeyData
.Data
= Allocator::standard().malloc
<uint8
>(keyDataLength
);
1559 rawKey
.KeyData
.Length
= keyDataLength
;
1560 memcpy(rawKey
.KeyData
.Data
, flatKey
.Data
+ sizeof(CSSM_KEY
), keyDataLength
);
1561 Security::n2hi(rawKey
.KeyHeader
); // convert it to host byte order
1565 KeychainDatabase::keyFromKeybag(const TypedList
&sample
)
1567 service_context_t context
;
1568 uint8_t *session_key
;
1569 int session_key_size
;
1571 const struct ccmode_siv
*mode
= ccaes_siv_decrypt_mode();
1572 const size_t session_key_wrapped_len
= 40;
1573 const size_t version_len
= 1, nonce_len
= 16;
1574 uint8_t *decrypted_data
;
1575 size_t decrypted_len
;
1577 assert(sample
.type() == CSSM_SAMPLE_TYPE_KEYBAG_KEY
);
1579 CssmData
&unlock_token
= sample
[2].data();
1581 context
= common().session().get_current_service_context();
1582 rc
= service_client_kb_unwrap_key(&context
, unlock_token
.data(), session_key_wrapped_len
, key_class_ak
, (void **)&session_key
, &session_key_size
);
1584 CssmError::throwMe(CSSM_ERRCODE_INVALID_CRYPTO_DATA
);
1587 uint8_t *indata
= (uint8_t *)unlock_token
.data() + session_key_wrapped_len
;
1588 size_t inlen
= unlock_token
.length() - session_key_wrapped_len
;
1590 decrypted_len
= ccsiv_plaintext_size(mode
, inlen
- (version_len
+ nonce_len
));
1591 decrypted_data
= (uint8_t *)calloc(1, decrypted_len
);
1593 ccsiv_ctx_decl(mode
->size
, ctx
);
1595 rc
= ccsiv_init(mode
, ctx
, session_key_size
, session_key
);
1596 if (rc
!= 0) CssmError::throwMe(CSSM_ERRCODE_INVALID_CRYPTO_DATA
);
1597 rc
= ccsiv_set_nonce(mode
, ctx
, nonce_len
, indata
+ version_len
);
1598 if (rc
!= 0) CssmError::throwMe(CSSM_ERRCODE_INVALID_CRYPTO_DATA
);
1599 rc
= ccsiv_aad(mode
, ctx
, 1, indata
);
1600 if (rc
!= 0) CssmError::throwMe(CSSM_ERRCODE_INVALID_CRYPTO_DATA
);
1601 rc
= ccsiv_crypt(mode
, ctx
, inlen
- (version_len
+ nonce_len
), indata
+ version_len
+ nonce_len
, decrypted_data
);
1602 if (rc
!= 0) CssmError::throwMe(CSSM_ERRCODE_INVALID_CRYPTO_DATA
);
1604 ccsiv_ctx_clear(mode
->size
, ctx
);
1606 //free(decrypted_data);
1608 return makeRawKey(decrypted_data
, decrypted_len
, CSSM_ALGID_3DES_3KEY_EDE
, CSSM_KEYUSE_ENCRYPT
| CSSM_KEYUSE_DECRYPT
);
1611 // adapted from DatabaseCryptoCore::makeRawKey
1612 CssmClient::Key
KeychainDatabase::makeRawKey(void *data
, size_t length
,
1613 CSSM_ALGORITHMS algid
, CSSM_KEYUSE usage
)
1617 key
.header().BlobType
= CSSM_KEYBLOB_RAW
;
1618 key
.header().Format
= CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING
;
1619 key
.header().AlgorithmId
= algid
;
1620 key
.header().KeyClass
= CSSM_KEYCLASS_SESSION_KEY
;
1621 key
.header().KeyUsage
= usage
;
1622 key
.header().KeyAttr
= 0;
1623 key
.KeyData
= CssmData(data
, length
);
1625 // unwrap it into the CSP (but keep it raw)
1626 CssmClient::UnwrapKey
unwrap(Server::csp(), CSSM_ALGID_NONE
);
1627 CssmKey unwrappedKey
;
1628 CssmData descriptiveData
;
1630 CssmClient::KeySpec(CSSM_KEYUSE_ANY
, CSSM_KEYATTR_RETURN_DATA
| CSSM_KEYATTR_EXTRACTABLE
),
1631 unwrappedKey
, &descriptiveData
, NULL
);
1632 return CssmClient::Key(Server::csp(), unwrappedKey
);
1636 // Verify a putative database passphrase.
1637 // If the database is already unlocked, just check the passphrase.
1638 // Otherwise, unlock with that passphrase and report success.
1639 // Caller must hold the common lock.
1641 bool KeychainDatabase::validatePassphrase(const CssmData
&passphrase
) const
1643 if (common().hasMaster()) {
1644 // verify against known secret
1645 return common().validatePassphrase(passphrase
);
1647 // no master secret - perform "blind" unlock to avoid actual unlock
1649 DatabaseCryptoCore test
;
1650 test
.setup(mBlob
, passphrase
);
1651 test
.decodeCore(mBlob
, NULL
);
1661 // Lock this database
1663 void KeychainDatabase::lockDb()
1670 // Given a Key for this database, encode it into a blob and return it.
1672 KeyBlob
*KeychainDatabase::encodeKey(const CssmKey
&key
, const CssmData
&pubAcl
, const CssmData
&privAcl
)
1674 bool inTheClear
= false;
1675 if((key
.keyClass() == CSSM_KEYCLASS_PUBLIC_KEY
) &&
1676 !(key
.attribute(CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT
))) {
1679 StLock
<Mutex
> _(common());
1681 makeUnlocked(false);
1683 // tell the cryptocore to form the key blob
1684 return common().encodeKeyCore(key
, pubAcl
, privAcl
, inTheClear
);
1689 // Given a "blobbed" key for this database, decode it into its real
1690 // key object and (re)populate its ACL.
1692 void KeychainDatabase::decodeKey(KeyBlob
*blob
, CssmKey
&key
, void * &pubAcl
, void * &privAcl
)
1694 StLock
<Mutex
> _(common());
1696 if(!blob
->isClearText())
1697 makeUnlocked(false); // we need our keys
1699 common().decodeKeyCore(blob
, key
, pubAcl
, privAcl
);
1700 // memory protocol: pubAcl points into blob; privAcl was allocated
1706 // Given a KeychainKey (that implicitly belongs to another keychain),
1707 // return it encoded using this keychain's operational secrets.
1709 KeyBlob
*KeychainDatabase::recodeKey(KeychainKey
&oldKey
)
1711 if (mRecodingSource
!= &oldKey
.referent
<KeychainDatabase
>()) {
1712 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
1715 // To protect this operation, we need to take the mutex for both our common and the remote key's common in some defined order.
1716 // Grab the common being cloned (oldKey's) first, and then the common receiving the recoding (ours).
1717 StLock
<Mutex
> _ (oldKey
.referent
<KeychainDatabase
>().common());
1718 StLock
<Mutex
> __(common());
1720 oldKey
.instantiateAcl(); // make sure key is decoded
1721 CssmData publicAcl
, privateAcl
;
1722 oldKey
.exportBlob(publicAcl
, privateAcl
);
1723 // NB: blob's memory belongs to caller, not the common
1726 * Make sure the new key is in the same cleartext/encrypted state.
1728 bool inTheClear
= false;
1729 assert(oldKey
.blob());
1730 if(oldKey
.blob() && oldKey
.blob()->isClearText()) {
1734 KeyBlob
*blob
= common().encodeKeyCore(oldKey
.cssmKey(), publicAcl
, privateAcl
, inTheClear
);
1735 oldKey
.acl().allocator
.free(publicAcl
);
1736 oldKey
.acl().allocator
.free(privateAcl
);
1742 // Modify database parameters
1744 void KeychainDatabase::setParameters(const DBParameters
¶ms
)
1746 StLock
<Mutex
> _(common());
1747 makeUnlocked(false);
1748 common().mParams
= params
;
1749 common().invalidateBlob(); // invalidate old blobs
1750 activity(); // (also resets the timeout timer)
1751 secinfo("KCdb", "%p common %p(%s) set params=(%u,%u)",
1752 this, &common(), dbName(), params
.idleTimeout
, params
.lockOnSleep
);
1757 // Retrieve database parameters
1759 void KeychainDatabase::getParameters(DBParameters
¶ms
)
1761 StLock
<Mutex
> _(common());
1762 makeUnlocked(false);
1763 params
= common().mParams
;
1764 //activity(); // getting parameters does not reset the idle timer
1769 // RIGHT NOW, database ACLs are attached to the database.
1770 // This will soon move upstairs.
1772 SecurityServerAcl
&KeychainDatabase::acl()
1779 // Intercept ACL change requests and reset blob validity
1781 void KeychainDatabase::instantiateAcl()
1783 StLock
<Mutex
> _(common());
1784 makeUnlocked(false);
1787 void KeychainDatabase::changedAcl()
1789 StLock
<Mutex
> _(common());
1795 // Check an incoming DbBlob for basic viability
1797 void KeychainDatabase::validateBlob(const DbBlob
*blob
)
1799 // perform basic validation on the blob
1801 blob
->validate(CSSMERR_APPLEDL_INVALID_DATABASE_BLOB
);
1802 if (blob
->startCryptoBlob
> blob
->totalLength
) {
1803 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_DATABASE_BLOB
);
1805 switch (blob
->version()) {
1806 #if defined(COMPAT_OSX_10_0)
1807 case DbBlob::version_MacOS_10_0
:
1810 case DbBlob::version_MacOS_10_1
:
1812 case DbBlob::version_partition
:
1815 CssmError::throwMe(CSSMERR_APPLEDL_INCOMPATIBLE_DATABASE_BLOB
);
1820 // Check if this database is currently recoding
1822 bool KeychainDatabase::isRecoding()
1824 secnotice("integrity", "recoding source: %p", mRecodingSource
.get());
1825 return (mRecodingSource
.get() != NULL
|| mRecoded
);
1829 // Mark ourselves as no longer recoding
1831 void KeychainDatabase::recodeFinished()
1833 secnotice("integrity", "recoding finished");
1834 mRecodingSource
= NULL
;
1840 // Debugging support
1842 #if defined(DEBUGDUMP)
1844 void KeychainDbCommon::dumpNode()
1846 PerSession::dumpNode();
1847 uint32 sig
; memcpy(&sig
, &mIdentifier
.signature(), sizeof(sig
));
1848 Debug::dump(" %s[%8.8x]", mIdentifier
.dbName(), sig
);
1850 Debug::dump(" locked");
1852 time_t whenTime
= time_t(when());
1853 Debug::dump(" unlocked(%24.24s/%.2g)", ctime(&whenTime
),
1854 (when() - Time::now()).seconds());
1856 Debug::dump(" params=(%u,%u)", mParams
.idleTimeout
, mParams
.lockOnSleep
);
1859 void KeychainDatabase::dumpNode()
1861 PerProcess::dumpNode();
1862 Debug::dump(" %s vers=%u",
1863 mValidData
? " data" : " nodata", version
);
1865 uint32 sig
; memcpy(&sig
, &mBlob
->randomSignature
, sizeof(sig
));
1866 Debug::dump(" blob=%p[%8.8x]", mBlob
, sig
);
1868 Debug::dump(" noblob");
1876 // DbCommon basic features
1878 KeychainDbCommon::KeychainDbCommon(Session
&ssn
, const DbIdentifier
&id
, uint32 requestedVersion
)
1879 : LocalDbCommon(ssn
), DatabaseCryptoCore(requestedVersion
), sequence(0), version(1), mIdentifier(id
),
1880 mIsLocked(true), mValidParams(false), mLoginKeychain(false)
1882 // match existing DbGlobal or create a new one
1884 Server
&server
= Server::active();
1885 StLock
<Mutex
> _(server
);
1886 if (KeychainDbGlobal
*dbglobal
=
1887 server
.findFirst
<KeychainDbGlobal
, const DbIdentifier
&>(&KeychainDbGlobal::identifier
, identifier())) {
1889 secinfo("KCdb", "%p linking to existing DbGlobal %p", this, dbglobal
);
1891 // DbGlobal not present; make a new one
1892 parent(*new KeychainDbGlobal(identifier()));
1893 secinfo("KCdb", "%p linking to new DbGlobal %p", this, &global());
1896 // link lifetime to the Session
1897 session().addReference(*this);
1899 if (strcasestr(id
.dbName(), "login.keychain") != NULL
) {
1900 mLoginKeychain
= true;
1905 void KeychainDbCommon::initializeKeybag() {
1906 if (mLoginKeychain
&& !session().keybagGetState(session_keybag_loaded
)) {
1907 service_context_t context
= session().get_current_service_context();
1908 if (service_client_kb_load(&context
) == 0) {
1909 session().keybagSetState(session_keybag_loaded
);
1914 KeychainDbCommon::KeychainDbCommon(Session
&ssn
, const DbIdentifier
&id
, KeychainDbCommon
& toClone
)
1915 : LocalDbCommon(ssn
), DatabaseCryptoCore(toClone
.mBlobVersion
), sequence(toClone
.sequence
), mParams(toClone
.mParams
), version(toClone
.version
),
1916 mIdentifier(id
), mIsLocked(toClone
.mIsLocked
), mValidParams(toClone
.mValidParams
), mLoginKeychain(toClone
.mLoginKeychain
)
1921 Server
&server
= Server::active();
1922 StLock
<Mutex
> _(server
);
1923 if (KeychainDbGlobal
*dbglobal
=
1924 server
.findFirst
<KeychainDbGlobal
, const DbIdentifier
&>(&KeychainDbGlobal::identifier
, identifier())) {
1926 secinfo("KCdb", "%p linking to existing DbGlobal %p", this, dbglobal
);
1928 // DbGlobal not present; make a new one
1929 parent(*new KeychainDbGlobal(identifier()));
1930 secinfo("KCdb", "%p linking to new DbGlobal %p", this, &global());
1932 session().addReference(*this);
1936 KeychainDbCommon::~KeychainDbCommon()
1938 secinfo("KCdb", "releasing keychain %p %s", this, (char*)this->dbName());
1940 // explicitly unschedule ourselves
1941 Server::active().clearTimer(this);
1942 if (mLoginKeychain
) {
1943 session().keybagClearState(session_keybag_unlocked
);
1945 // remove ourselves from mCommonSet
1949 void KeychainDbCommon::cloneFrom(KeychainDbCommon
& toClone
, uint32 requestedVersion
) {
1950 // don't clone the mIdentifier
1951 sequence
= toClone
.sequence
;
1952 mParams
= toClone
.mParams
;
1953 version
= toClone
.version
;
1954 mIsLocked
= toClone
.mIsLocked
;
1955 mValidParams
= toClone
.mValidParams
;
1956 mLoginKeychain
= toClone
.mLoginKeychain
;
1958 DatabaseCryptoCore::initializeFrom(toClone
, requestedVersion
);
1961 void KeychainDbCommon::kill()
1963 StReadWriteLock
_(mRWCommonLock
, StReadWriteLock::Write
);
1964 mCommonSet
.erase(this);
1967 KeychainDbGlobal
&KeychainDbCommon::global() const
1969 return parent
<KeychainDbGlobal
>();
1973 void KeychainDbCommon::select()
1976 void KeychainDbCommon::unselect()
1981 void KeychainDbCommon::makeNewSecrets()
1983 // we already have a master key (right?)
1984 assert(hasMaster());
1986 // tell crypto core to generate the use keys
1987 DatabaseCryptoCore::generateNewSecrets();
1989 // we're now officially "unlocked"; set the timer
1995 // All unlocking activity ultimately funnels through this method.
1996 // This unlocks a DbCommon using the secrets setup in its crypto core
1997 // component, and performs all the housekeeping needed to represent
1998 // the state change.
1999 // Returns true if unlock was successful, false if it failed due to
2000 // invalid/insufficient secrets. Throws on other errors.
2002 bool KeychainDbCommon::unlockDb(DbBlob
*blob
, void **privateAclBlob
)
2005 // Tell the cryptocore to (try to) decode itself. This will fail
2006 // in an astonishing variety of ways if the passphrase is wrong.
2007 assert(hasMaster());
2008 decodeCore(blob
, privateAclBlob
);
2009 secinfo("KCdb", "%p unlock successful", this);
2011 secinfo("KCdb", "%p unlock failed", this);
2015 // get the database parameters only if we haven't got them yet
2016 if (!mValidParams
) {
2017 mParams
= blob
->params
;
2018 n2hi(mParams
.idleTimeout
);
2019 mValidParams
= true; // sticky
2022 bool isLocked
= mIsLocked
;
2024 setUnlocked(); // mark unlocked
2027 // broadcast unlock notification, but only if we were previously locked
2028 notify(kNotificationEventUnlocked
);
2029 secinfo("KCdb", "unlocking keychain %p %s", this, (char*)this->dbName());
2034 void KeychainDbCommon::setUnlocked()
2036 session().addReference(*this); // active/held
2037 mIsLocked
= false; // mark unlocked
2038 activity(); // set timeout timer
2042 void KeychainDbCommon::lockDb()
2045 StLock
<Mutex
> _(*this);
2047 DatabaseCryptoCore::invalidate();
2048 notify(kNotificationEventLocked
);
2049 secinfo("KCdb", "locking keychain %p %s", this, (char*)this->dbName());
2050 Server::active().clearTimer(this);
2052 mIsLocked
= true; // mark locked
2054 // this call may destroy us if we have no databases anymore
2055 session().removeReference(*this);
2061 DbBlob
*KeychainDbCommon::encode(KeychainDatabase
&db
)
2063 assert(!isLocked()); // must have been unlocked by caller
2065 // export database ACL to blob form
2066 CssmData pubAcl
, privAcl
;
2067 db
.acl().exportBlob(pubAcl
, privAcl
);
2069 // tell the cryptocore to form the blob
2071 form
.randomSignature
= identifier();
2072 form
.sequence
= sequence
;
2073 form
.params
= mParams
;
2074 h2ni(form
.params
.idleTimeout
);
2076 assert(hasMaster());
2077 DbBlob
*blob
= encodeCore(form
, pubAcl
, privAcl
);
2080 db
.acl().allocator
.free(pubAcl
);
2081 db
.acl().allocator
.free(privAcl
);
2087 // Perform deferred lock processing for a database.
2089 void KeychainDbCommon::action()
2091 secinfo("KCdb", "common %s(%p) locked by timer", dbName(), this);
2095 void KeychainDbCommon::activity()
2098 secinfo("KCdb", "setting DbCommon %p timer to %d",
2099 this, int(mParams
.idleTimeout
));
2100 Server::active().setTimer(this, Time::Interval(int(mParams
.idleTimeout
)));
2104 void KeychainDbCommon::sleepProcessing()
2106 secinfo("KCdb", "common %s(%p) sleep-lock processing", dbName(), this);
2107 if (mParams
.lockOnSleep
&& !isDefaultSystemKeychain()) {
2108 StLock
<Mutex
> _(*this);
2113 void KeychainDbCommon::lockProcessing()
2120 // We consider a keychain to belong to the system domain if it resides
2121 // in /Library/Keychains. That's not exactly fool-proof, but we don't
2122 // currently have any internal markers to interrogate.
2124 bool KeychainDbCommon::belongsToSystem() const
2126 if (const char *name
= this->dbName())
2127 return !strncmp(name
, "/Library/Keychains/", 19);
2131 bool KeychainDbCommon::isDefaultSystemKeychain() const
2133 // /Library/Keychains/System.keychain (34)
2134 if (const char *name
= this->dbName())
2135 return !strncmp(name
, "/Library/Keychains/System.keychain", 34);
2140 // Keychain global objects
2142 KeychainDbGlobal::KeychainDbGlobal(const DbIdentifier
&id
)
2147 KeychainDbGlobal::~KeychainDbGlobal()
2149 secinfo("KCdb", "DbGlobal %p destroyed", this);