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 "SecRandom.h"
47 #include <vector> // @@@ 4003540 workaround
48 #include <security_cdsa_utilities/acl_any.h> // for default owner ACLs
49 #include <security_cdsa_utilities/cssmendian.h>
50 #include <security_cdsa_client/wrapkey.h>
51 #include <security_cdsa_client/genkey.h>
52 #include <security_cdsa_client/signclient.h>
53 #include <security_cdsa_client/cryptoclient.h>
54 #include <security_cdsa_client/macclient.h>
55 #include <securityd_client/dictionary.h>
56 #include <security_utilities/endian.h>
57 #include <security_utilities/errors.h>
58 #include "securityd_service/securityd_service/securityd_service_client.h"
59 #include <AssertMacros.h>
61 #include <sys/sysctl.h>
62 #include <sys/kauth.h>
65 #include <corecrypto/ccmode_siv.h>
68 void unflattenKey(const CssmData
&flatKey
, CssmKey
&rawKey
); //>> make static method on KeychainDatabase
73 KeychainDbCommon::CommonSet
KeychainDbCommon::mCommonSet
;
74 ReadWriteLock
KeychainDbCommon::mRWCommonLock
;
76 // Process is using a cached effective uid, login window switches uid after the intial connection
77 static void get_process_euid(pid_t pid
, uid_t
* out_euid
)
79 if (!out_euid
) return;
81 struct kinfo_proc proc_info
= {};
82 int mib
[] = {CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, pid
};
83 size_t len
= sizeof(struct kinfo_proc
);
85 ret
= sysctl(mib
, (sizeof(mib
)/sizeof(int)), &proc_info
, &len
, NULL
, 0);
88 if ((ret
== 0) && (proc_info
.kp_eproc
.e_ucred
.cr_uid
!= 0)) {
89 *out_euid
= proc_info
.kp_eproc
.e_ucred
.cr_uid
;
94 unlock_keybag(KeychainDatabase
& db
, const void * secret
, int secret_len
)
98 if (!db
.common().isLoginKeychain()) return 0;
100 service_context_t context
= db
.common().session().get_current_service_context();
102 // login window attempts to change the password before a session has a uid
103 if (context
.s_uid
== AU_DEFAUDITID
) {
104 get_process_euid(db
.process().pid(), &context
.s_uid
);
107 // try to unlock first if not found then load/create or unlock
108 // loading should happen when the kb common object is created
109 // if it doesn't exist yet then the unlock will fail and we'll create everything
110 rc
= service_client_kb_unlock(&context
, secret
, secret_len
);
111 if (rc
== KB_BagNotLoaded
) {
112 if (service_client_kb_load(&context
) == KB_BagNotFound
) {
113 rc
= service_client_kb_create(&context
, secret
, secret_len
);
115 rc
= service_client_kb_unlock(&context
, secret
, secret_len
);
119 if (rc
!= 0) { // if we just upgraded make sure we swap the encryption key to the password
120 if (!db
.common().session().keybagGetState(session_keybag_check_master_key
)) {
121 CssmAutoData
encKey(Allocator::standard(Allocator::sensitive
));
122 db
.common().get_encryption_key(encKey
);
123 if ((rc
= service_client_kb_unlock(&context
, encKey
.data(), (int)encKey
.length())) == 0) {
124 rc
= service_client_kb_change_secret(&context
, encKey
.data(), (int)encKey
.length(), secret
, secret_len
);
127 if (rc
!= 0) { // if a login.keychain password exists but doesnt on the keybag update it
129 if ((secret_len
> 0) && service_client_kb_is_locked(&context
, NULL
, &no_pin
) == 0) {
131 syslog(LOG_ERR
, "Updating iCloud keychain passphrase for uid %d", context
.s_uid
);
132 service_client_kb_change_secret(&context
, NULL
, 0, secret
, secret_len
);
136 } // session_keybag_check_master_key
140 db
.common().session().keybagSetState(session_keybag_unlocked
|session_keybag_loaded
|session_keybag_check_master_key
);
142 syslog(LOG_ERR
, "Failed to unlock iCloud keychain for uid %d", context
.s_uid
);
149 change_secret_on_keybag(KeychainDatabase
& db
, const void * secret
, int secret_len
, const void * new_secret
, int new_secret_len
)
151 if (!db
.common().isLoginKeychain()) return;
153 service_context_t context
= db
.common().session().get_current_service_context();
155 // login window attempts to change the password before a session has a uid
156 if (context
.s_uid
== AU_DEFAUDITID
) {
157 get_process_euid(db
.process().pid(), &context
.s_uid
);
160 // if a login.keychain doesn't exist yet it comes into securityd as a create then change_secret
161 // we need to create the keybag in this case if it doesn't exist
162 int rc
= service_client_kb_change_secret(&context
, secret
, secret_len
, new_secret
, new_secret_len
);
163 if (rc
== KB_BagNotLoaded
) {
164 if (service_client_kb_load(&context
) == KB_BagNotFound
) {
165 rc
= service_client_kb_create(&context
, new_secret
, new_secret_len
);
167 rc
= service_client_kb_change_secret(&context
, secret
, secret_len
, new_secret
, new_secret_len
);
171 // this makes it possible to restore a deleted keybag on condition it still exists in kernel
172 if (rc
!= KB_Success
) {
173 service_client_kb_save(&context
);
176 // if for some reason we are locked lets unlock so later we don't try and throw up SecurityAgent dialog
178 if ((service_client_kb_is_locked(&context
, &locked
, NULL
) == KB_Success
) && locked
) {
179 rc
= service_client_kb_unlock(&context
, new_secret
, new_secret_len
);
180 if (rc
!= KB_Success
) {
181 syslog(LOG_ERR
, "Failed to unlock iCloud keychain for uid %d (%d)", context
.s_uid
, (int)rc
);
186 // Attempt to unlock the keybag with a AccessCredentials password.
187 // Honors UI disabled flags from clients set in the cred before prompt.
189 unlock_keybag_with_cred(KeychainDatabase
&db
, const AccessCredentials
*cred
){
190 list
<CssmSample
> samples
;
191 if (cred
&& cred
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
, samples
)) {
192 for (list
<CssmSample
>::iterator it
= samples
.begin(); it
!= samples
.end(); it
++) {
193 TypedList
&sample
= *it
;
194 sample
.checkProper();
195 switch (sample
.type()) {
196 // interactively prompt the user - no additional data
197 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
:
200 Okay, this is messy. We need to hold the common lock to be certain we're modifying the world
201 as we intend. But UI ^ common, and QueryKeybagPassphrase::query() has tons of side effects by necessity,
202 so just confirm that the operation did what we wanted after the fact.
204 bool query_success
= false;
205 bool unlock_success
= false;
209 StSyncLock
<Mutex
, Mutex
> uisync(db
.common().uiLock(), db
.common());
210 // Once we get the ui lock, check whether another thread has already unlocked keybag
212 query_success
= false;
213 service_context_t context
= db
.common().session().get_current_service_context();
214 if ((service_client_kb_is_locked(&context
, &locked
, NULL
) == 0) && locked
) {
215 QueryKeybagPassphrase
keybagQuery(db
.common().session(), 3);
216 keybagQuery
.inferHints(Server::process());
217 if (keybagQuery
.query() == SecurityAgent::noReason
) {
218 query_success
= true;
221 // another thread already unlocked the keybag
222 query_success
= true; // NOT unlock_success because we have the wrong lock
224 } // StSyncLock goes out of scope, we have common lock again
226 service_context_t context
= db
.common().session().get_current_service_context();
227 if ((service_client_kb_is_locked(&context
, &locked
, NULL
) == 0) && !locked
) {
228 unlock_success
= true;
231 secnotice("KCdb", "Unlocking the keybag again (threading?)");
234 } while (query_success
&& !unlock_success
);
237 // try to use an explicitly given passphrase - Data:passphrase
238 case CSSM_SAMPLE_TYPE_PASSWORD
: {
239 if (sample
.length() != 2)
240 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
241 secinfo("KCdb", "attempting passphrase unlock of keybag");
242 if (unlock_keybag(db
, sample
[1].data().data(), (int)sample
[1].data().length())) {
248 // Unknown sub-sample for unlocking.
249 secinfo("KCdb", "keybag: unknown sub-sample unlock (%d) ignored", sample
.type());
259 // Create a Database object from initial parameters (create operation)
261 KeychainDatabase::KeychainDatabase(const DLDbIdentifier
&id
, const DBParameters
¶ms
, Process
&proc
,
262 const AccessCredentials
*cred
, const AclEntryPrototype
*owner
)
263 : LocalDatabase(proc
), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive
)), mSaveSecret(false), version(0), mBlob(NULL
), mRecoded(false)
265 // save a copy of the credentials for later access control
266 mCred
= DataWalkers::copy(cred
, Allocator::standard());
268 // create a new random signature to complete the DLDbIdentifier
269 DbBlob::Signature newSig
;
271 (MacOSError::check
)(SecRandomCopyBytes(kSecRandomDefault
, sizeof(newSig
.bytes
), newSig
.bytes
));
272 DbIdentifier
ident(id
, newSig
);
274 // create common block and initialize
275 // Since this is a creation step, figure out the correct blob version for this database
276 RefPointer
<KeychainDbCommon
> newCommon
= new KeychainDbCommon(proc
.session(), ident
, CommonBlob::getCurrentVersionForDb(ident
.dbName()));
277 newCommon
->initializeKeybag();
279 StLock
<Mutex
> _(*newCommon
);
282 // new common is now visible (in ident-map) but we hold its lock
284 // establish the new master secret
285 establishNewSecrets(cred
, SecurityAgent::newDatabase
, false);
287 // set initial database parameters
288 common().mParams
= params
;
290 // the common is "unlocked" now
291 common().makeNewSecrets();
293 // establish initial ACL
295 acl().cssmSetInitial(*owner
);
297 acl().cssmSetInitial(new AnyAclSubject());
300 // for now, create the blob immediately
303 proc
.addReference(*this);
305 // this new keychain is unlocked; make it so
308 secinfo("KCdb", "creating keychain %p %s with common %p", this, (char*)this->dbName(), &common());
313 // Create a Database object from a database blob (decoding)
315 KeychainDatabase::KeychainDatabase(const DLDbIdentifier
&id
, const DbBlob
*blob
, Process
&proc
,
316 const AccessCredentials
*cred
)
317 : LocalDatabase(proc
), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive
)), mSaveSecret(false), version(0), mBlob(NULL
), mRecoded(false)
321 // save a copy of the credentials for later access control
322 mCred
= DataWalkers::copy(cred
, Allocator::standard());
323 mBlob
= blob
->copy();
325 // check to see if we already know about this database
326 DbIdentifier
ident(id
, blob
->randomSignature
);
327 Session
&session
= process().session();
328 RefPointer
<KeychainDbCommon
> com
;
329 secinfo("kccommon", "looking for a common at %s", ident
.dbName());
330 if (KeychainDbCommon::find(ident
, session
, com
)) {
332 secinfo("KCdb", "joining keychain %p %s with common %p", this, (char*)this->dbName(), &common());
334 // DbCommon not present; make a new one
335 secinfo("kccommon", "no common found");
337 common().mParams
= blob
->params
;
338 secinfo("KCdb", "making keychain %p %s with common %p", this, (char*)this->dbName(), &common());
339 // this DbCommon is locked; no timer or reference setting
341 proc
.addReference(*this);
344 void KeychainDbCommon::insert()
346 StReadWriteLock
_(mRWCommonLock
, StReadWriteLock::Write
);
350 void KeychainDbCommon::insertHoldingLock()
352 mCommonSet
.insert(this);
357 // find or make a DbCommon. Returns true if an existing one was found and used.
358 bool KeychainDbCommon::find(const DbIdentifier
&ident
, Session
&session
, RefPointer
<KeychainDbCommon
> &common
, uint32 requestedVersion
, KeychainDbCommon
* cloneFrom
)
360 // Prepare to drop the mRWCommonLock.
362 StReadWriteLock
_(mRWCommonLock
, StReadWriteLock::Read
);
363 for (CommonSet::const_iterator it
= mCommonSet
.begin(); it
!= mCommonSet
.end(); ++it
) {
364 if (&session
== &(*it
)->session() && ident
== (*it
)->identifier()) {
366 secinfo("kccommon", "found a common for %s at %p", ident
.dbName(), common
.get());
372 // not found. Grab the write lock, ensure that nobody has beaten us to adding,
373 // and then create a DbCommon and add it to the map.
375 StReadWriteLock
_(mRWCommonLock
, StReadWriteLock::Write
);
376 for (CommonSet::const_iterator it
= mCommonSet
.begin(); it
!= mCommonSet
.end(); ++it
) {
377 if (&session
== &(*it
)->session() && ident
== (*it
)->identifier()) {
379 secinfo("kccommon", "found a common for %s at %p", ident
.dbName(), common
.get());
386 common
= new KeychainDbCommon(session
, ident
, *cloneFrom
);
387 } else if(requestedVersion
!= CommonBlob::version_none
) {
388 common
= new KeychainDbCommon(session
, ident
, requestedVersion
);
390 common
= new KeychainDbCommon(session
, ident
);
393 secinfo("kccommon", "made a new common for %s at %p", ident
.dbName(), common
.get());
395 // Can't call insert() here, because it grabs the write lock (which we have).
396 common
->insertHoldingLock();
398 common
->initializeKeybag();
404 // Special-purpose constructor for keychain synchronization. Copies an
405 // existing keychain but uses the operational keys from secretsBlob. The
406 // new KeychainDatabase will silently replace the existing KeychainDatabase
407 // as soon as the client declares that re-encoding of all keychain items is
408 // finished. This is a little perilous since it allows a client to dictate
409 // securityd state, but we try to ensure that only the client that started
410 // the re-encoding can declare it done.
412 KeychainDatabase::KeychainDatabase(KeychainDatabase
&src
, Process
&proc
, DbHandle dbToClone
)
413 : LocalDatabase(proc
), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive
)), mSaveSecret(false), version(0), mBlob(NULL
), mRecoded(false)
415 mCred
= DataWalkers::copy(src
.mCred
, Allocator::standard());
417 // Give this KeychainDatabase a temporary name
418 std::string newDbName
= std::string("////") + std::string(src
.identifier().dbName());
419 DLDbIdentifier
newDLDbIdent(src
.identifier().dlDbIdentifier().ssuid(), newDbName
.c_str(), src
.identifier().dlDbIdentifier().dbLocation());
420 DbIdentifier
ident(newDLDbIdent
, src
.identifier());
422 // create common block and initialize
423 RefPointer
<KeychainDbCommon
> newCommon
= new KeychainDbCommon(proc
.session(), ident
);
424 newCommon
->initializeKeybag();
425 StLock
<Mutex
> _(*newCommon
);
429 // set initial database parameters from the source keychain
430 common().mParams
= src
.common().mParams
;
432 // establish the source keychain's master secret as ours
433 // @@@ NB: this is a v. 0.1 assumption. We *should* trigger new UI
434 // that offers the user the option of using the existing password
435 // or choosing a new one. That would require a new
436 // SecurityAgentQuery type, new UI, and--possibly--modifications to
437 // ensure that the new password is available here to generate the
438 // new master secret.
439 src
.unlockDb(false); // precaution for masterKey()
440 common().setup(src
.blob(), src
.common().masterKey());
442 // import the operational secrets
443 RefPointer
<KeychainDatabase
> srcKC
= Server::keychain(dbToClone
);
444 common().importSecrets(srcKC
->common());
446 // import source keychain's ACL
447 CssmData pubAcl
, privAcl
;
448 src
.acl().exportBlob(pubAcl
, privAcl
);
449 importBlob(pubAcl
.data(), privAcl
.data());
450 src
.acl().allocator
.free(pubAcl
);
451 src
.acl().allocator
.free(privAcl
);
453 // indicate that this keychain should be allowed to do some otherwise
454 // risky things required for copying, like re-encoding keys
455 mRecodingSource
= &src
;
457 common().setUnlocked();
462 proc
.addReference(*this);
463 secinfo("SSdb", "database %s(%p) created as copy, common at %p",
464 common().dbName(), this, &common());
467 // Make a new KeychainDatabase from an old one, but have a completely different location
468 KeychainDatabase::KeychainDatabase(const DLDbIdentifier
& id
, KeychainDatabase
&src
, Process
&proc
)
469 : LocalDatabase(proc
), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive
)), mSaveSecret(false), version(0), mBlob(NULL
), mRecoded(false)
471 mCred
= DataWalkers::copy(src
.mCred
, Allocator::standard());
473 DbIdentifier
ident(id
, src
.identifier());
475 // create common block and initialize
476 RefPointer
<KeychainDbCommon
> newCommon
;
477 if(KeychainDbCommon::find(ident
, process().session(), newCommon
, CommonBlob::version_none
, &src
.common())) {
478 // A common already existed. Write over it, but note that everything may go horribly from here on out.
479 secinfo("kccommon", "Found common where we didn't expect. Possible strange behavior ahead.");
480 newCommon
->cloneFrom(src
.common());
483 StLock
<Mutex
> _(*newCommon
);
486 // set initial database parameters from the source keychain
487 common().mParams
= src
.common().mParams
;
489 // import source keychain's ACL
490 CssmData pubAcl
, privAcl
;
491 src
.acl().exportBlob(pubAcl
, privAcl
);
492 importBlob(pubAcl
.data(), privAcl
.data());
493 src
.acl().allocator
.free(pubAcl
);
494 src
.acl().allocator
.free(privAcl
);
496 // Copy the source database's blob, if possible
498 mBlob
= src
.mBlob
->copy();
499 version
= src
.version
;
502 // We've copied everything we can from our source. If they were valid, so are we.
503 mValidData
= src
.mValidData
;
505 proc
.addReference(*this);
506 secinfo("SSdb", "database %s(%p) created as expected clone, common at %p", common().dbName(), this, &common());
510 // Make a new KeychainDatabase from an old one, but have entirely new operational secrets
511 KeychainDatabase::KeychainDatabase(uint32 requestedVersion
, KeychainDatabase
&src
, Process
&proc
)
512 : LocalDatabase(proc
), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive
)), mSaveSecret(false), version(0), mBlob(NULL
), mRecoded(false)
514 mCred
= DataWalkers::copy(src
.mCred
, Allocator::standard());
516 // Give this KeychainDatabase a temporary name
517 // this must canonicalize to a different path than the original DB, otherwise another process opening the existing DB wil find this new KeychainDbCommon
518 // and call decodeCore with the old blob, overwriting the new secrets and wreaking havoc
519 std::string newDbName
= std::string("////") + std::string(src
.identifier().dbName()) + std::string("_com.apple.security.keychain.migrating");
520 DLDbIdentifier
newDLDbIdent(src
.identifier().dlDbIdentifier().ssuid(), newDbName
.c_str(), src
.identifier().dlDbIdentifier().dbLocation());
521 DbIdentifier
ident(newDLDbIdent
, src
.identifier());
523 // hold the lock for src's common during this operation (to match locking common locking order with KeychainDatabase::recodeKey)
524 StLock
<Mutex
> __(src
.common());
526 // create common block and initialize
527 RefPointer
<KeychainDbCommon
> newCommon
;
528 if(KeychainDbCommon::find(ident
, process().session(), newCommon
, requestedVersion
)) {
529 // A common already existed here. Write over it, but note that everything may go horribly from here on out.
530 secinfo("kccommon", "Found common where we didn't expect. Possible strange behavior ahead.");
531 newCommon
->cloneFrom(src
.common(), requestedVersion
);
533 newCommon
->initializeKeybag();
534 StLock
<Mutex
> _(*newCommon
);
537 // We want to re-use the master secrets from the source database (and so the
538 // same password), but reroll new operational secrets.
540 // Copy the master secret over...
541 src
.unlockDb(false); // precaution
543 common().setup(src
.blob(), src
.common().masterKey(), false); // keep the new common's version intact
545 // set initial database parameters from the source keychain
546 common().mParams
= src
.common().mParams
;
548 // and make new operational secrets
549 common().makeNewSecrets();
551 // import source keychain's ACL
552 CssmData pubAcl
, privAcl
;
553 src
.acl().exportBlob(pubAcl
, privAcl
);
554 importBlob(pubAcl
.data(), privAcl
.data());
555 src
.acl().allocator
.free(pubAcl
);
556 src
.acl().allocator
.free(privAcl
);
558 // indicate that this keychain should be allowed to do some otherwise
559 // risky things required for copying, like re-encoding keys
560 mRecodingSource
= &src
;
562 common().setUnlocked();
567 proc
.addReference(*this);
568 secinfo("SSdb", "database %s(%p) created as expected copy, common at %p",
569 common().dbName(), this, &common());
573 // Destroy a Database
575 KeychainDatabase::~KeychainDatabase()
577 secinfo("KCdb", "deleting database %s(%p) common %p",
578 common().dbName(), this, &common());
579 Allocator::standard().free(mCred
);
580 Allocator::standard().free(mBlob
);
585 // Basic Database virtual implementations
587 KeychainDbCommon
&KeychainDatabase::common() const
589 return parent
<KeychainDbCommon
>();
592 const char *KeychainDatabase::dbName() const
594 return common().dbName();
597 bool KeychainDatabase::transient() const
599 return false; // has permanent store
602 AclKind
KeychainDatabase::aclKind() const
607 Database
*KeychainDatabase::relatedDatabase()
613 // (Re-)Authenticate the database. This changes the stored credentials.
615 void KeychainDatabase::authenticate(CSSM_DB_ACCESS_TYPE mode
,
616 const AccessCredentials
*cred
)
618 StLock
<Mutex
> _(common());
620 // the (Apple specific) RESET bit means "lock the database now"
622 case CSSM_DB_ACCESS_RESET
:
623 secinfo("KCdb", "%p ACCESS_RESET triggers keychain lock", this);
627 // store the new credentials for future use
628 secinfo("KCdb", "%p authenticate stores new database credentials", this);
629 AccessCredentials
*newCred
= DataWalkers::copy(cred
, Allocator::standard());
630 Allocator::standard().free(mCred
);
637 // Make a new KeychainKey.
638 // If PERMANENT is off, make a temporary key instead.
639 // The db argument allows you to create for another KeychainDatabase (only);
640 // it defaults to ourselves.
642 RefPointer
<Key
> KeychainDatabase::makeKey(Database
&db
, const CssmKey
&newKey
,
643 uint32 moreAttributes
, const AclEntryPrototype
*owner
)
645 StLock
<Mutex
> lock(common());
646 if (moreAttributes
& CSSM_KEYATTR_PERMANENT
)
647 return new KeychainKey(db
, newKey
, moreAttributes
, owner
);
649 return process().makeTemporaryKey(newKey
, moreAttributes
, owner
);
652 RefPointer
<Key
> KeychainDatabase::makeKey(const CssmKey
&newKey
,
653 uint32 moreAttributes
, const AclEntryPrototype
*owner
)
655 return makeKey(*this, newKey
, moreAttributes
, owner
);
660 // Return the database blob, recalculating it as needed.
662 DbBlob
*KeychainDatabase::blob()
664 StLock
<Mutex
> _(common());
666 makeUnlocked(false); // unlock to get master secret
667 encode(); // (re)encode blob if needed
669 activity(); // reset timeout
670 assert(validBlob()); // better have a valid blob now...
676 // Encode the current database as a blob.
677 // Note that this returns memory we own and keep.
678 // Caller must hold common lock.
680 void KeychainDatabase::encode()
682 DbBlob
*blob
= common().encode(*this);
683 Allocator::standard().free(mBlob
);
685 version
= common().version
;
686 secinfo("KCdb", "encoded database %p common %p(%s) version %u params=(%u,%u)",
687 this, &common(), dbName(), version
,
688 common().mParams
.idleTimeout
, common().mParams
.lockOnSleep
);
692 // Change the passphrase on a database
694 void KeychainDatabase::changePassphrase(const AccessCredentials
*cred
)
696 // get and hold the common lock (don't let other threads break in here)
697 StLock
<Mutex
> _(common());
699 list
<CssmSample
> samples
;
700 bool hasOldSecret
= cred
&& cred
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
, samples
);
701 samples
.clear(); // Can't count the samples at the end because I could specify CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK twice
702 bool hasNewSecret
= cred
&& cred
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
, samples
);
704 // If no creds we do interactive, if both we do silently. If unequal, think harder.
705 if (hasOldSecret
!= hasNewSecret
) {
706 if (!hasOldSecret
&& !isLocked()) {
707 // Alternative: do a confirm_access SA dialog and run validatePassphrase on it (what client name?)
708 // That's risky for now, but it would cover the remaining use case.
709 secerror("KCdb: changePassphrase credential set has no old secret and KC not locked");
710 MacOSError::throwMe(errSecAuthFailed
);
714 secnotice("KCdb", "changePassphrase proceeding with old %i, new %i, locked %i", hasOldSecret
, hasNewSecret
, isLocked());
716 if (hasOldSecret
&& !checkCredentials(cred
)) {
717 secinfo("KCdb", "Cannot change passphrase for (%s): existing passphrase does not match", common().dbName());
718 MacOSError::throwMe(errSecAuthFailed
);
721 // establish OLD secret - i.e. unlock the database. You'll be prompted if !hasOldSecrets and DB is locked.
722 if (common().isLoginKeychain()) mSaveSecret
= true;
723 makeUnlocked(cred
, false);
725 // establish NEW secret
726 if(!establishNewSecrets(cred
, SecurityAgent::changePassphrase
, true)) {
727 secinfo("KCdb", "Old and new passphrases are the same. Database %s(%p) master secret did not change.",
728 common().dbName(), this);
731 if (mSecret
) { mSecret
.reset(); }
733 common().invalidateBlob(); // blob state changed
734 secinfo("KCdb", "Database %s(%p) master secret changed", common().dbName(), this);
735 encode(); // force rebuild of local blob
737 // send out a notification
738 notify(kNotificationEventPassphraseChanged
);
740 // I guess this counts as an activity
745 // Second stage of keychain synchronization: overwrite the original keychain's
746 // (this KeychainDatabase's) operational secrets
748 void KeychainDatabase::commitSecretsForSync(KeychainDatabase
&cloneDb
)
750 StLock
<Mutex
> _(common());
752 // try to detect spoofing
753 if (cloneDb
.mRecodingSource
!= this)
754 CssmError::throwMe(CSSM_ERRCODE_INVALID_DB_HANDLE
);
756 // in case we autolocked since starting the sync
757 makeUnlocked(false); // call this because we already own the lock
758 cloneDb
.unlockDb(false); // we may not own the lock here, so calling unlockDb will lock the cloneDb's common lock
760 // Decode all keys whose handles refer to this on-disk keychain so that
761 // if the holding client commits the key back to disk, it's encoded with
762 // the new operational secrets. The recoding client *must* hold a write
763 // lock for the on-disk keychain from the moment it starts recoding key
764 // items until after this call.
766 // @@@ This specific implementation is a workaround for 4003540.
767 std::vector
<U32HandleObject::Handle
> handleList
;
768 U32HandleObject::findAllRefs
<KeychainKey
>(handleList
);
769 size_t count
= handleList
.size();
771 for (unsigned int n
= 0; n
< count
; ++n
) {
772 RefPointer
<KeychainKey
> kckey
=
773 U32HandleObject::findRefAndLock
<KeychainKey
>(handleList
[n
], CSSMERR_CSP_INVALID_KEY_REFERENCE
);
774 StLock
<Mutex
> _(*kckey
/*, true*/);
775 if (kckey
->database().global().identifier() == identifier()) {
776 kckey
->key(); // force decode
777 kckey
->invalidateBlob();
778 secinfo("kcrecode", "changed extant key %p (proc %d)",
779 &*kckey
, kckey
->process().pid());
784 // mark down that we just recoded
787 // it is now safe to replace the old op secrets
788 common().importSecrets(cloneDb
.common());
789 common().invalidateBlob();
794 // Extract the database master key as a proper Key object.
796 RefPointer
<Key
> KeychainDatabase::extractMasterKey(Database
&db
,
797 const AccessCredentials
*cred
, const AclEntryPrototype
*owner
,
798 uint32 usage
, uint32 attrs
)
800 // get and hold common lock
801 StLock
<Mutex
> _(common());
803 // force lock to require re-validation of credentials
806 // unlock to establish master secret
809 // extract the raw cryptographic key
810 CssmClient::WrapKey
wrap(Server::csp(), CSSM_ALGID_NONE
);
812 wrap(common().masterKey(), key
);
814 // make the key object and return it
815 return makeKey(db
, key
, attrs
& LocalKey::managedAttributes
, owner
);
820 // Unlock this database (if needed) by obtaining the master secret in some
821 // suitable way and then proceeding to unlock with it.
822 // Does absolutely nothing if the database is already unlocked.
823 // The makeUnlocked forms are identical except the assume the caller already
824 // holds the common lock.
826 void KeychainDatabase::unlockDb(bool unlockKeybag
)
828 StLock
<Mutex
> _(common());
829 makeUnlocked(unlockKeybag
);
832 void KeychainDatabase::makeUnlocked(bool unlockKeybag
)
834 return makeUnlocked(mCred
, unlockKeybag
);
837 void KeychainDatabase::makeUnlocked(const AccessCredentials
*cred
, bool unlockKeybag
)
840 secnotice("KCdb", "%p(%p) unlocking for makeUnlocked()", this, &common());
841 assert(mBlob
|| (mValidData
&& common().hasMaster()));
842 bool asking_again
= false;
845 secnotice("KCdb", "makeUnlocked: establishing old secrets again (threading?)");
847 establishOldSecrets(cred
);
849 } while (!common().hasMaster());
850 common().setUnlocked(); // mark unlocked
851 if (common().isLoginKeychain()) {
852 CssmKey master
= common().masterKey();
854 CssmClient::WrapKey
wrap(Server::csp(), CSSM_ALGID_NONE
);
855 wrap(master
, rawMaster
);
857 service_context_t context
= common().session().get_current_service_context();
858 service_client_stash_load_key(&context
, rawMaster
.keyData(), (int)rawMaster
.length());
860 } else if (unlockKeybag
&& common().isLoginKeychain()) {
862 service_context_t context
= common().session().get_current_service_context();
863 if ((service_client_kb_is_locked(&context
, &locked
, NULL
) == 0) && locked
) {
864 if (!unlock_keybag_with_cred(*this, cred
)) {
865 syslog(LOG_NOTICE
, "failed to unlock iCloud keychain");
869 if (!mValidData
) { // need to decode to get our ACLs, master secret available
870 secnotice("KCdb", "%p(%p) is unlocked; decoding for makeUnlocked()", this, &common());
872 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
879 // Invoke the securityd_service to retrieve the keychain master
880 // key from the AppleFDEKeyStore.
882 void KeychainDatabase::stashDbCheck()
884 CssmAutoData
masterKey(Allocator::standard(Allocator::sensitive
));
885 CssmAutoData
encKey(Allocator::standard(Allocator::sensitive
));
889 void * stash_key
= NULL
;
890 int stash_key_len
= 0;
891 service_context_t context
= common().session().get_current_service_context();
892 rc
= service_client_stash_get_key(&context
, &stash_key
, &stash_key_len
);
895 masterKey
.copy(CssmData((void *)stash_key
,stash_key_len
));
896 memset(stash_key
, 0, stash_key_len
);
900 secnotice("KCdb", "failed to get stash from securityd_service: %d", (int)rc
);
901 CssmError::throwMe(rc
);
905 StLock
<Mutex
> _(common());
907 // Now establish it as the keychain master key
908 CssmClient::Key
key(Server::csp(), masterKey
.get());
909 CssmKey::Header
&hdr
= key
.header();
910 hdr
.keyClass(CSSM_KEYCLASS_SESSION_KEY
);
911 hdr
.algorithm(CSSM_ALGID_3DES_3KEY_EDE
);
912 hdr
.usage(CSSM_KEYUSE_ANY
);
913 hdr
.blobType(CSSM_KEYBLOB_RAW
);
914 hdr
.blobFormat(CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING
);
915 common().setup(mBlob
, key
);
918 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
920 common().get_encryption_key(encKey
);
923 // when upgrading from pre-10.9 create a keybag if it doesn't exist with the encryption key
924 // only do this after we have verified the master key unlocks the login.keychain
925 if (service_client_kb_load(&context
) == KB_BagNotFound
) {
926 service_client_kb_create(&context
, encKey
.data(), (int)encKey
.length());
931 // Get the keychain master key and invoke the securityd_service
932 // to stash it in the AppleFDEKeyStore ready for commit to the
935 void KeychainDatabase::stashDb()
937 CssmAutoData
data(Allocator::standard(Allocator::sensitive
));
940 StLock
<Mutex
> _(common());
942 if (!common().isValid()) {
943 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
946 CssmKey key
= common().masterKey();
947 data
.copy(key
.keyData());
950 service_context_t context
= common().session().get_current_service_context();
951 int rc
= service_client_stash_set_key(&context
, data
.data(), (int)data
.length());
952 if (rc
!= 0) CssmError::throwMe(rc
);
956 // The following unlock given an explicit passphrase, rather than using
957 // (special cred sample based) default procedures.
959 void KeychainDatabase::unlockDb(const CssmData
&passphrase
, bool unlockKeybag
)
961 StLock
<Mutex
> _(common());
962 makeUnlocked(passphrase
, unlockKeybag
);
965 void KeychainDatabase::makeUnlocked(const CssmData
&passphrase
, bool unlockKeybag
)
968 if (decode(passphrase
))
971 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
972 } else if (!mValidData
) { // need to decode to get our ACLs, passphrase available
974 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
977 if (unlockKeybag
&& common().isLoginKeychain()) {
979 service_context_t context
= common().session().get_current_service_context();
980 if (!common().session().keybagGetState(session_keybag_check_master_key
) || ((service_client_kb_is_locked(&context
, &locked
, NULL
) == 0) && locked
)) {
981 unlock_keybag(*this, passphrase
.data(), (int)passphrase
.length());
991 // Nonthrowing passphrase-based unlock. This returns false if unlock failed.
992 // Note that this requires an explicitly given passphrase.
993 // Caller must hold common lock.
995 bool KeychainDatabase::decode(const CssmData
&passphrase
)
998 common().setup(mBlob
, passphrase
);
999 bool success
= decode();
1000 if (success
&& common().isLoginKeychain()) {
1001 unlock_keybag(*this, passphrase
.data(), (int)passphrase
.length());
1008 // Given the established master secret, decode the working keys and other
1009 // functional secrets for this database. Return false (do NOT throw) if
1010 // the decode fails. Call this in low(er) level code once you established
1013 bool KeychainDatabase::decode()
1016 assert(common().hasMaster());
1017 void *privateAclBlob
;
1018 if (common().unlockDb(mBlob
, &privateAclBlob
)) {
1020 acl().importBlob(mBlob
->publicAclBlob(), privateAclBlob
);
1023 Allocator::standard().free(privateAclBlob
);
1026 secinfo("KCdb", "%p decode failed", this);
1032 // Given an AccessCredentials for this database, wring out the existing primary
1033 // database secret by whatever means necessary.
1034 // On entry, caller must hold the database common lock. It will be held
1035 // throughout except when user interaction is required. User interaction
1036 // requires relinquishing the database common lock and taking the UI lock. On
1037 // return from user interaction, the UI lock is relinquished and the database
1038 // common lock must be reacquired. At no time may the caller hold both locks.
1039 // On exit, the crypto core has its master secret. If things go wrong,
1040 // we will throw a suitable exception. Note that encountering any malformed
1041 // credential sample will throw, but this is not guaranteed -- don't assume
1042 // that NOT throwing means creds is entirely well-formed (it may just be good
1043 // enough to work THIS time).
1046 // Walk through the creds. Fish out those credentials (in order) that
1047 // are for unlock processing (they have no ACL subject correspondents),
1048 // and (try to) obey each in turn, until one produces a valid secret
1049 // or you run out. If no special samples are found at all, interpret that as
1050 // "use the system global default," which happens to be hard-coded right here.
1052 void KeychainDatabase::establishOldSecrets(const AccessCredentials
*creds
)
1054 bool forSystem
= this->belongsToSystem(); // this keychain belongs to the system security domain
1056 // attempt system-keychain unlock
1058 SystemKeychainKey
systemKeychain(kSystemUnlockFile
);
1059 if (systemKeychain
.matches(mBlob
->randomSignature
)) {
1060 secinfo("KCdb", "%p attempting system unlock", this);
1061 common().setup(mBlob
, CssmClient::Key(Server::csp(), systemKeychain
.key(), true));
1067 list
<CssmSample
> samples
;
1068 if (creds
&& creds
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
, samples
)) {
1069 for (list
<CssmSample
>::iterator it
= samples
.begin(); it
!= samples
.end(); it
++) {
1070 TypedList
&sample
= *it
;
1071 sample
.checkProper();
1072 switch (sample
.type()) {
1073 // interactively prompt the user - no additional data
1074 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
:
1076 if (interactiveUnlock())
1080 // try to use an explicitly given passphrase - Data:passphrase
1081 case CSSM_SAMPLE_TYPE_PASSWORD
:
1082 if (sample
.length() != 2)
1083 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1084 secinfo("KCdb", "%p attempting passphrase unlock", this);
1085 if (decode(sample
[1]))
1088 // try to open with a given master key - Data:CSP or KeyHandle, Data:CssmKey
1089 case CSSM_SAMPLE_TYPE_SYMMETRIC_KEY
:
1090 case CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY
:
1092 secinfo("KCdb", "%p attempting explicit key unlock", this);
1093 common().setup(mBlob
, keyFromCreds(sample
, 4));
1098 case CSSM_SAMPLE_TYPE_KEYBAG_KEY
:
1100 secinfo("KCdb", "%p attempting keybag key unlock", this);
1101 common().setup(mBlob
, keyFromKeybag(sample
));
1106 // explicitly defeat the default action but don't try anything in particular
1107 case CSSM_WORDID_CANCELED
:
1108 secinfo("KCdb", "%p defeat default action", this);
1111 // Unknown sub-sample for unlocking.
1112 // If we wanted to be fascist, we could now do
1113 // CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
1114 // But instead we try to be tolerant and continue on.
1115 // This DOES however count as an explicit attempt at specifying unlock,
1116 // so we will no longer try the default case below...
1117 secinfo("KCdb", "%p unknown sub-sample unlock (%d) ignored", this, sample
.type());
1126 if (interactiveUnlock())
1131 // out of options - no secret obtained
1132 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
1136 // This function is almost identical to establishOldSecrets, but:
1137 // 1. It will never prompt the user; these credentials either work or they don't
1138 // 2. It will not change the secrets of this database
1140 // TODO: These two functions should probably be refactored to something nicer.
1141 bool KeychainDatabase::checkCredentials(const AccessCredentials
*creds
) {
1143 list
<CssmSample
> samples
;
1144 if (creds
&& creds
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
, samples
)) {
1145 for (list
<CssmSample
>::iterator it
= samples
.begin(); it
!= samples
.end(); it
++) {
1146 TypedList
&sample
= *it
;
1147 sample
.checkProper();
1148 switch (sample
.type()) {
1149 // interactively prompt the user - no additional data
1150 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
:
1151 // do nothing, because this function will never prompt the user
1152 secinfo("integrity", "%p ignoring keychain prompt", this);
1154 // try to use an explicitly given passphrase - Data:passphrase
1155 case CSSM_SAMPLE_TYPE_PASSWORD
:
1156 if (sample
.length() != 2)
1157 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1158 secinfo("integrity", "%p checking passphrase", this);
1159 if(validatePassphrase(sample
[1])) {
1163 // try to open with a given master key - Data:CSP or KeyHandle, Data:CssmKey
1164 case CSSM_SAMPLE_TYPE_SYMMETRIC_KEY
:
1165 case CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY
:
1167 secinfo("integrity", "%p attempting explicit key unlock", this);
1169 CssmClient::Key checkKey
= keyFromCreds(sample
, 4);
1170 if(common().validateKey(checkKey
)) {
1174 // ignore all problems in keyFromCreds
1175 secinfo("integrity", "%p caught error", this);
1182 // out of options - credentials don't match
1186 uint32_t KeychainDatabase::interactiveUnlockAttempts
= 0;
1188 // 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!
1189 // Try to hold the UI lock for the smallest amount of time possible while having the common lock where needed.
1190 bool KeychainDatabase::interactiveUnlock()
1192 secinfo("KCdb", "%p attempting interactive unlock", this);
1193 interactiveUnlockAttempts
++;
1195 SecurityAgent::Reason reason
= SecurityAgent::noReason
;
1196 QueryUnlock
query(*this);
1199 query
.inferHints(Server::process());
1200 StSyncLock
<Mutex
, Mutex
> uisync(common().uiLock(), common());
1203 if (mSaveSecret
&& reason
== SecurityAgent::noReason
) {
1204 query
.retrievePassword(mSecret
);
1208 secinfo("KCdb", "%p was unlocked during uiLock delay", this);
1211 if (common().isLoginKeychain()) {
1212 bool locked
= false;
1213 service_context_t context
= common().session().get_current_service_context();
1214 if ((service_client_kb_is_locked(&context
, &locked
, NULL
) == 0) && locked
) {
1215 QueryKeybagNewPassphrase
keybagQuery(common().session());
1216 keybagQuery
.inferHints(Server::process());
1217 CssmAutoData
pass(Allocator::standard(Allocator::sensitive
));
1218 CssmAutoData
oldPass(Allocator::standard(Allocator::sensitive
));
1219 StSyncLock
<Mutex
, Mutex
> uisync(common().uiLock(), common());
1220 SecurityAgent::Reason queryReason
= keybagQuery
.query(oldPass
, pass
);
1222 if (queryReason
== SecurityAgent::noReason
) {
1223 service_client_kb_change_secret(&context
, oldPass
.data(), (int)oldPass
.length(), pass
.data(), (int)pass
.length());
1224 } else if (queryReason
== SecurityAgent::resettingPassword
) {
1225 query
.retrievePassword(pass
);
1226 service_client_kb_reset(&context
, pass
.data(), (int)pass
.length());
1232 return reason
== SecurityAgent::noReason
;
1235 uint32_t KeychainDatabase::getInteractiveUnlockAttempts() {
1236 if (csr_check(CSR_ALLOW_APPLE_INTERNAL
)) {
1237 // Not an internal install; don't answer
1240 return interactiveUnlockAttempts
;
1246 // Same thing, but obtain a new secret somehow and set it into the common.
1248 bool KeychainDatabase::establishNewSecrets(const AccessCredentials
*creds
, SecurityAgent::Reason reason
, bool change
)
1250 list
<CssmSample
> samples
;
1251 if (creds
&& creds
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
, samples
)) {
1252 for (list
<CssmSample
>::iterator it
= samples
.begin(); it
!= samples
.end(); it
++) {
1253 TypedList
&sample
= *it
;
1254 sample
.checkProper();
1255 switch (sample
.type()) {
1256 // interactively prompt the user
1257 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
:
1259 secinfo("KCdb", "%p specified interactive passphrase", this);
1260 QueryNewPassphrase
query(*this, reason
);
1261 StSyncLock
<Mutex
, Mutex
> uisync(common().uiLock(), common());
1262 query
.inferHints(Server::process());
1263 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
1264 CssmAutoData
oldPassphrase(Allocator::standard(Allocator::sensitive
));
1265 SecurityAgent::Reason
reason(query(oldPassphrase
, passphrase
));
1267 if (reason
== SecurityAgent::noReason
) {
1268 common().setup(NULL
, passphrase
);
1269 change_secret_on_keybag(*this, oldPassphrase
.data(), (int)oldPassphrase
.length(), passphrase
.data(), (int)passphrase
.length());
1274 // try to use an explicitly given passphrase
1275 case CSSM_SAMPLE_TYPE_PASSWORD
:
1277 secinfo("KCdb", "%p specified explicit passphrase", this);
1278 if (sample
.length() != 2)
1279 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1280 if (common().isLoginKeychain()) {
1281 CssmAutoData
oldPassphrase(Allocator::standard(Allocator::sensitive
));
1282 list
<CssmSample
> oldSamples
;
1283 creds
->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
, oldSamples
);
1284 for (list
<CssmSample
>::iterator oit
= oldSamples
.begin(); oit
!= oldSamples
.end(); oit
++) {
1285 TypedList
&tmpList
= *oit
;
1286 tmpList
.checkProper();
1287 if (tmpList
.type() == CSSM_SAMPLE_TYPE_PASSWORD
) {
1288 if (tmpList
.length() == 2) {
1289 oldPassphrase
= tmpList
[1].data();
1293 if (!oldPassphrase
.length() && mSecret
&& mSecret
.length()) {
1294 oldPassphrase
= mSecret
;
1296 if ((oldPassphrase
.length() == sample
[1].data().length()) &&
1297 !memcmp(oldPassphrase
.data(), sample
[1].data().data(), oldPassphrase
.length()) &&
1298 oldPassphrase
.length()) {
1299 // don't change master key if the passwords are the same
1302 common().setup(NULL
, sample
[1]);
1303 change_secret_on_keybag(*this, oldPassphrase
.data(), (int)oldPassphrase
.length(), sample
[1].data().data(), (int)sample
[1].data().length());
1306 common().setup(NULL
, sample
[1]);
1310 // try to open with a given master key
1311 case CSSM_WORDID_SYMMETRIC_KEY
:
1312 case CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY
:
1313 secinfo("KCdb", "%p specified explicit master key", this);
1314 common().setup(NULL
, keyFromCreds(sample
, 3));
1316 // explicitly defeat the default action but don't try anything in particular
1317 case CSSM_WORDID_CANCELED
:
1318 secinfo("KCdb", "%p defeat default action", this);
1321 // Unknown sub-sample for acquiring new secret.
1322 // If we wanted to be fascist, we could now do
1323 // CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
1324 // But instead we try to be tolerant and continue on.
1325 // This DOES however count as an explicit attempt at specifying unlock,
1326 // so we will no longer try the default case below...
1327 secinfo("KCdb", "%p unknown sub-sample acquisition (%d) ignored",
1328 this, sample
.type());
1333 // default action -- interactive (only)
1334 QueryNewPassphrase
query(*this, reason
);
1335 StSyncLock
<Mutex
, Mutex
> uisync(common().uiLock(), common());
1336 query
.inferHints(Server::process());
1337 CssmAutoData
passphrase(Allocator::standard(Allocator::sensitive
));
1338 CssmAutoData
oldPassphrase(Allocator::standard(Allocator::sensitive
));
1339 SecurityAgent::Reason
reason(query(oldPassphrase
, passphrase
));
1341 if (reason
== SecurityAgent::noReason
) {
1342 // If the DB was already unlocked then we have not yet checked the caller knows the old passphrase. Do so here.
1343 if (change
&& !validatePassphrase(oldPassphrase
.get())) {
1344 MacOSError::throwMe(errSecAuthFailed
);
1346 common().setup(NULL
, passphrase
);
1347 change_secret_on_keybag(*this, oldPassphrase
.data(), (int)oldPassphrase
.length(), passphrase
.data(), (int)passphrase
.length());
1352 // out of options - no secret obtained
1353 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
1358 // Given a (truncated) Database credentials TypedList specifying a master key,
1359 // locate the key and return a reference to it.
1361 CssmClient::Key
KeychainDatabase::keyFromCreds(const TypedList
&sample
, unsigned int requiredLength
)
1363 // decode TypedList structure (sample type; Data:CSPHandle; Data:CSSM_KEY)
1364 assert(sample
.type() == CSSM_SAMPLE_TYPE_SYMMETRIC_KEY
|| sample
.type() == CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY
);
1365 if (sample
.length() != requiredLength
1366 || sample
[1].type() != CSSM_LIST_ELEMENT_DATUM
1367 || sample
[2].type() != CSSM_LIST_ELEMENT_DATUM
1368 || (requiredLength
== 4 && sample
[3].type() != CSSM_LIST_ELEMENT_DATUM
))
1369 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1370 KeyHandle
&handle
= *sample
[1].data().interpretedAs
<KeyHandle
>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1371 // We used to be able to check the length but supporting multiple client
1372 // architectures dishes that (sizeof(CSSM_KEY) varies due to alignment and
1373 // field-size differences). The decoding in the transition layer should
1374 // serve as a sufficient garbling check anyway.
1375 if (sample
[2].data().data() == NULL
)
1376 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1377 CssmKey
&key
= *sample
[2].data().interpretedAs
<CssmKey
>();
1379 if (key
.header().cspGuid() == gGuidAppleCSPDL
) {
1380 // handleOrKey is a SecurityServer KeyHandle; ignore key argument
1381 return safer_cast
<LocalKey
&>(*Server::key(handle
));
1383 if (sample
.type() == CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY
) {
1385 Contents (see DefaultCredentials::unlockKey in libsecurity_keychain/defaultcreds.cpp)
1387 sample[0] sample type
1388 sample[1] csp handle for master or wrapping key; is really a keyhandle
1389 sample[2] masterKey [not used since securityd cannot interpret; use sample[1] handle instead]
1390 sample[3] UnlockReferralRecord data, in this case the flattened symmetric key
1393 // RefPointer<Key> Server::key(KeyHandle key)
1394 KeyHandle keyhandle
= *sample
[1].data().interpretedAs
<KeyHandle
>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE
);
1395 CssmData
&flattenedKey
= sample
[3].data();
1396 RefPointer
<Key
> unwrappingKey
= Server::key(keyhandle
);
1397 Database
&db
=unwrappingKey
->database();
1399 CssmKey rawWrappedKey
;
1400 unflattenKey(flattenedKey
, rawWrappedKey
);
1402 RefPointer
<Key
> masterKey
;
1403 CssmData emptyDescriptiveData
;
1404 const AccessCredentials
*cred
= NULL
;
1405 const AclEntryPrototype
*owner
= NULL
;
1406 CSSM_KEYUSE usage
= CSSM_KEYUSE_ANY
;
1407 CSSM_KEYATTR_FLAGS attrs
= CSSM_KEYATTR_EXTRACTABLE
; //CSSM_KEYATTR_RETURN_REF |
1409 // Get default credentials for unwrappingKey (the one on the token)
1410 // Copied from Statics::Statics() in libsecurity_keychain/aclclient.cpp
1411 // Following KeyItem::getCredentials, one sees that the "operation" parameter
1412 // e.g. "CSSM_ACL_AUTHORIZATION_DECRYPT" is ignored
1413 Allocator
&alloc
= Allocator::standard();
1414 AutoCredentials
promptCred(alloc
, 3);// enable interactive prompting
1416 // promptCred: a credential permitting user prompt confirmations
1418 // a KEYCHAIN_PROMPT sample, both by itself and in a THRESHOLD
1419 // a PROMPTED_PASSWORD sample
1420 promptCred
.sample(0) = TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
);
1421 promptCred
.sample(1) = TypedList(alloc
, CSSM_SAMPLE_TYPE_THRESHOLD
,
1422 new(alloc
) ListElement(TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
)));
1423 promptCred
.sample(2) = TypedList(alloc
, CSSM_SAMPLE_TYPE_PROMPTED_PASSWORD
,
1424 new(alloc
) ListElement(alloc
, CssmData()));
1426 // This unwrap object is here just to provide a context
1427 CssmClient::UnwrapKey
unwrap(Server::csp(), CSSM_ALGID_NONE
); //ok to lie about csp here
1428 unwrap
.mode(CSSM_ALGMODE_NONE
);
1429 unwrap
.padding(CSSM_PADDING_PKCS1
);
1430 unwrap
.cred(promptCred
);
1431 unwrap
.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT
, uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7
));
1432 Security::Context
*tmpContext
;
1433 CSSM_CC_HANDLE CCHandle
= unwrap
.handle();
1434 /*CSSM_RETURN rx = */ CSSM_GetContext (CCHandle
, (CSSM_CONTEXT_PTR
*)&tmpContext
);
1436 // OK, this is skanky but necessary. We overwrite fields in the context struct
1438 tmpContext
->ContextType
= CSSM_ALGCLASS_ASYMMETRIC
;
1439 tmpContext
->AlgorithmType
= CSSM_ALGID_RSA
;
1441 db
.unwrapKey(*tmpContext
, cred
, owner
, unwrappingKey
, NULL
, usage
, attrs
,
1442 rawWrappedKey
, masterKey
, emptyDescriptiveData
);
1444 Allocator::standard().free(rawWrappedKey
.KeyData
.Data
);
1446 return safer_cast
<LocalKey
&>(*masterKey
).key();
1448 else if (sample
.type() == CSSM_SAMPLE_TYPE_SYMMETRIC_KEY
&& sample
.length() == 4 && sample
[3].data().length() > 0) {
1450 Contents (see MasterKeyUnlockCredentials in libsecurity_cdsa_client/lib/aclclient.cpp)
1452 sample[0] sample type
1453 sample[1] 0, since we don't have a valid handle
1454 sample[2] CssmKey of the masterKey [can't immediately use since it includes a CSSM_DATA struct with pointers]
1455 sample[3] flattened symmetric master key, including the key data
1458 // Fix up key to include actual data
1459 CssmData
&flattenedKey
= sample
[3].data();
1460 unflattenKey(flattenedKey
, key
);
1462 // Check that we have a reasonable key
1463 if (key
.header().blobType() != CSSM_KEYBLOB_RAW
) {
1464 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE
);
1466 if (key
.header().keyClass() != CSSM_KEYCLASS_SESSION_KEY
) {
1467 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
1470 // bring the key into the CSP and return it
1471 return CssmClient::Key(Server::csp(), key
, true);
1473 // not a KeyHandle reference; use key as a raw key
1474 if (key
.header().blobType() != CSSM_KEYBLOB_RAW
)
1475 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE
);
1476 if (key
.header().keyClass() != CSSM_KEYCLASS_SESSION_KEY
)
1477 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
1478 return CssmClient::Key(Server::csp(), key
, true);
1482 void unflattenKey(const CssmData
&flatKey
, CssmKey
&rawKey
)
1484 // The format we're expecting is a CSSM_KEY followed by the actual key data:
1485 // CSSM_KEY : KEY DATA
1486 // which is approximately:
1487 // h2ni(CSSM_KEYHEADER) : 4 bytes padding : CSSM_DATA{?:?} : KEY BYTES
1489 // Note that CSSM_KEY includes a CSSM_DATA struct, which we will ignore as it has pointers.
1490 // The pointer and length will be set to whatever key data follows the CSSM_KEY in rawKey.
1492 // unflatten the raw input key naively: key header then key data
1493 // We also convert it back to host byte order
1494 // A CSSM_KEY is a CSSM_KEYHEADER followed by a CSSM_DATA
1496 // Now copy: header, then key struct, then key data
1497 memcpy(&rawKey
.KeyHeader
, flatKey
.Data
, sizeof(CSSM_KEYHEADER
));
1498 memcpy(&rawKey
.KeyData
, flatKey
.Data
+ sizeof(CSSM_KEYHEADER
), sizeof(CSSM_DATA
));
1499 size_t keyDataLength
= flatKey
.length() - sizeof(CSSM_KEY
);
1500 rawKey
.KeyData
.Data
= Allocator::standard().malloc
<uint8
>(keyDataLength
);
1501 rawKey
.KeyData
.Length
= keyDataLength
;
1502 memcpy(rawKey
.KeyData
.Data
, flatKey
.Data
+ sizeof(CSSM_KEY
), keyDataLength
);
1503 Security::n2hi(rawKey
.KeyHeader
); // convert it to host byte order
1507 KeychainDatabase::keyFromKeybag(const TypedList
&sample
)
1509 service_context_t context
;
1510 uint8_t *session_key
;
1511 int session_key_size
;
1513 const struct ccmode_siv
*mode
= ccaes_siv_decrypt_mode();
1514 const size_t session_key_wrapped_len
= 40;
1515 const size_t version_len
= 1, nonce_len
= 16;
1516 uint8_t *decrypted_data
;
1517 size_t decrypted_len
;
1519 assert(sample
.type() == CSSM_SAMPLE_TYPE_KEYBAG_KEY
);
1521 CssmData
&unlock_token
= sample
[2].data();
1523 context
= common().session().get_current_service_context();
1524 rc
= service_client_kb_unwrap_key(&context
, unlock_token
.data(), session_key_wrapped_len
, key_class_ak
, (void **)&session_key
, &session_key_size
);
1526 CssmError::throwMe(CSSM_ERRCODE_INVALID_CRYPTO_DATA
);
1529 uint8_t *indata
= (uint8_t *)unlock_token
.data() + session_key_wrapped_len
;
1530 size_t inlen
= unlock_token
.length() - session_key_wrapped_len
;
1532 decrypted_len
= ccsiv_plaintext_size(mode
, inlen
- (version_len
+ nonce_len
));
1533 decrypted_data
= (uint8_t *)calloc(1, decrypted_len
);
1535 ccsiv_ctx_decl(mode
->size
, ctx
);
1537 rc
= ccsiv_init(mode
, ctx
, session_key_size
, session_key
);
1538 if (rc
!= 0) CssmError::throwMe(CSSM_ERRCODE_INVALID_CRYPTO_DATA
);
1539 rc
= ccsiv_set_nonce(mode
, ctx
, nonce_len
, indata
+ version_len
);
1540 if (rc
!= 0) CssmError::throwMe(CSSM_ERRCODE_INVALID_CRYPTO_DATA
);
1541 rc
= ccsiv_aad(mode
, ctx
, 1, indata
);
1542 if (rc
!= 0) CssmError::throwMe(CSSM_ERRCODE_INVALID_CRYPTO_DATA
);
1543 rc
= ccsiv_crypt(mode
, ctx
, inlen
- (version_len
+ nonce_len
), indata
+ version_len
+ nonce_len
, decrypted_data
);
1544 if (rc
!= 0) CssmError::throwMe(CSSM_ERRCODE_INVALID_CRYPTO_DATA
);
1546 ccsiv_ctx_clear(mode
->size
, ctx
);
1548 //free(decrypted_data);
1550 return makeRawKey(decrypted_data
, decrypted_len
, CSSM_ALGID_3DES_3KEY_EDE
, CSSM_KEYUSE_ENCRYPT
| CSSM_KEYUSE_DECRYPT
);
1553 // adapted from DatabaseCryptoCore::makeRawKey
1554 CssmClient::Key
KeychainDatabase::makeRawKey(void *data
, size_t length
,
1555 CSSM_ALGORITHMS algid
, CSSM_KEYUSE usage
)
1559 key
.header().BlobType
= CSSM_KEYBLOB_RAW
;
1560 key
.header().Format
= CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING
;
1561 key
.header().AlgorithmId
= algid
;
1562 key
.header().KeyClass
= CSSM_KEYCLASS_SESSION_KEY
;
1563 key
.header().KeyUsage
= usage
;
1564 key
.header().KeyAttr
= 0;
1565 key
.KeyData
= CssmData(data
, length
);
1567 // unwrap it into the CSP (but keep it raw)
1568 CssmClient::UnwrapKey
unwrap(Server::csp(), CSSM_ALGID_NONE
);
1569 CssmKey unwrappedKey
;
1570 CssmData descriptiveData
;
1572 CssmClient::KeySpec(CSSM_KEYUSE_ANY
, CSSM_KEYATTR_RETURN_DATA
| CSSM_KEYATTR_EXTRACTABLE
),
1573 unwrappedKey
, &descriptiveData
, NULL
);
1574 return CssmClient::Key(Server::csp(), unwrappedKey
);
1578 // Verify a putative database passphrase.
1579 // If the database is already unlocked, just check the passphrase.
1580 // Otherwise, unlock with that passphrase and report success.
1581 // Caller must hold the common lock.
1583 bool KeychainDatabase::validatePassphrase(const CssmData
&passphrase
) const
1585 if (common().hasMaster()) {
1586 // verify against known secret
1587 return common().validatePassphrase(passphrase
);
1589 // no master secret - perform "blind" unlock to avoid actual unlock
1591 DatabaseCryptoCore test
;
1592 test
.setup(mBlob
, passphrase
);
1593 test
.decodeCore(mBlob
, NULL
);
1603 // Lock this database
1605 void KeychainDatabase::lockDb()
1612 // Given a Key for this database, encode it into a blob and return it.
1614 KeyBlob
*KeychainDatabase::encodeKey(const CssmKey
&key
, const CssmData
&pubAcl
, const CssmData
&privAcl
)
1616 bool inTheClear
= false;
1617 if((key
.keyClass() == CSSM_KEYCLASS_PUBLIC_KEY
) &&
1618 !(key
.attribute(CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT
))) {
1621 StLock
<Mutex
> _(common());
1623 makeUnlocked(false);
1625 // tell the cryptocore to form the key blob
1626 return common().encodeKeyCore(key
, pubAcl
, privAcl
, inTheClear
);
1631 // Given a "blobbed" key for this database, decode it into its real
1632 // key object and (re)populate its ACL.
1634 void KeychainDatabase::decodeKey(KeyBlob
*blob
, CssmKey
&key
, void * &pubAcl
, void * &privAcl
)
1636 StLock
<Mutex
> _(common());
1638 if(!blob
->isClearText())
1639 makeUnlocked(false); // we need our keys
1641 common().decodeKeyCore(blob
, key
, pubAcl
, privAcl
);
1642 // memory protocol: pubAcl points into blob; privAcl was allocated
1648 // Given a KeychainKey (that implicitly belongs to another keychain),
1649 // return it encoded using this keychain's operational secrets.
1651 KeyBlob
*KeychainDatabase::recodeKey(KeychainKey
&oldKey
)
1653 if (mRecodingSource
!= &oldKey
.referent
<KeychainDatabase
>()) {
1654 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
1657 // To protect this operation, we need to take the mutex for both our common and the remote key's common in some defined order.
1658 // Grab the common being cloned (oldKey's) first, and then the common receiving the recoding (ours).
1659 StLock
<Mutex
> _ (oldKey
.referent
<KeychainDatabase
>().common());
1660 StLock
<Mutex
> __(common());
1662 oldKey
.instantiateAcl(); // make sure key is decoded
1663 CssmData publicAcl
, privateAcl
;
1664 oldKey
.exportBlob(publicAcl
, privateAcl
);
1665 // NB: blob's memory belongs to caller, not the common
1668 * Make sure the new key is in the same cleartext/encrypted state.
1670 bool inTheClear
= false;
1671 assert(oldKey
.blob());
1672 if(oldKey
.blob() && oldKey
.blob()->isClearText()) {
1676 KeyBlob
*blob
= common().encodeKeyCore(oldKey
.cssmKey(), publicAcl
, privateAcl
, inTheClear
);
1677 oldKey
.acl().allocator
.free(publicAcl
);
1678 oldKey
.acl().allocator
.free(privateAcl
);
1684 // Modify database parameters
1686 void KeychainDatabase::setParameters(const DBParameters
¶ms
)
1688 StLock
<Mutex
> _(common());
1689 makeUnlocked(false);
1690 common().mParams
= params
;
1691 common().invalidateBlob(); // invalidate old blobs
1692 activity(); // (also resets the timeout timer)
1693 secinfo("KCdb", "%p common %p(%s) set params=(%u,%u)",
1694 this, &common(), dbName(), params
.idleTimeout
, params
.lockOnSleep
);
1699 // Retrieve database parameters
1701 void KeychainDatabase::getParameters(DBParameters
¶ms
)
1703 StLock
<Mutex
> _(common());
1704 makeUnlocked(false);
1705 params
= common().mParams
;
1706 //activity(); // getting parameters does not reset the idle timer
1711 // RIGHT NOW, database ACLs are attached to the database.
1712 // This will soon move upstairs.
1714 SecurityServerAcl
&KeychainDatabase::acl()
1721 // Intercept ACL change requests and reset blob validity
1723 void KeychainDatabase::instantiateAcl()
1725 StLock
<Mutex
> _(common());
1726 makeUnlocked(false);
1729 void KeychainDatabase::changedAcl()
1731 StLock
<Mutex
> _(common());
1737 // Check an incoming DbBlob for basic viability
1739 void KeychainDatabase::validateBlob(const DbBlob
*blob
)
1741 // perform basic validation on the blob
1743 blob
->validate(CSSMERR_APPLEDL_INVALID_DATABASE_BLOB
);
1744 if (blob
->startCryptoBlob
> blob
->totalLength
) {
1745 CssmError::throwMe(CSSMERR_APPLEDL_INVALID_DATABASE_BLOB
);
1747 switch (blob
->version()) {
1748 #if defined(COMPAT_OSX_10_0)
1749 case DbBlob::version_MacOS_10_0
:
1752 case DbBlob::version_MacOS_10_1
:
1754 case DbBlob::version_partition
:
1757 CssmError::throwMe(CSSMERR_APPLEDL_INCOMPATIBLE_DATABASE_BLOB
);
1762 // Check if this database is currently recoding
1764 bool KeychainDatabase::isRecoding()
1766 secnotice("integrity", "recoding source: %p", mRecodingSource
.get());
1767 return (mRecodingSource
.get() != NULL
|| mRecoded
);
1771 // Mark ourselves as no longer recoding
1773 void KeychainDatabase::recodeFinished()
1775 secnotice("integrity", "recoding finished");
1776 mRecodingSource
= NULL
;
1782 // Debugging support
1784 #if defined(DEBUGDUMP)
1786 void KeychainDbCommon::dumpNode()
1788 PerSession::dumpNode();
1789 uint32 sig
; memcpy(&sig
, &mIdentifier
.signature(), sizeof(sig
));
1790 Debug::dump(" %s[%8.8x]", mIdentifier
.dbName(), sig
);
1792 Debug::dump(" locked");
1794 time_t whenTime
= time_t(when());
1795 Debug::dump(" unlocked(%24.24s/%.2g)", ctime(&whenTime
),
1796 (when() - Time::now()).seconds());
1798 Debug::dump(" params=(%u,%u)", mParams
.idleTimeout
, mParams
.lockOnSleep
);
1801 void KeychainDatabase::dumpNode()
1803 PerProcess::dumpNode();
1804 Debug::dump(" %s vers=%u",
1805 mValidData
? " data" : " nodata", version
);
1807 uint32 sig
; memcpy(&sig
, &mBlob
->randomSignature
, sizeof(sig
));
1808 Debug::dump(" blob=%p[%8.8x]", mBlob
, sig
);
1810 Debug::dump(" noblob");
1818 // DbCommon basic features
1820 KeychainDbCommon::KeychainDbCommon(Session
&ssn
, const DbIdentifier
&id
, uint32 requestedVersion
)
1821 : LocalDbCommon(ssn
), DatabaseCryptoCore(requestedVersion
), sequence(0), version(1), mIdentifier(id
),
1822 mIsLocked(true), mValidParams(false), mLoginKeychain(false)
1824 // match existing DbGlobal or create a new one
1826 Server
&server
= Server::active();
1827 StLock
<Mutex
> _(server
);
1828 if (KeychainDbGlobal
*dbglobal
=
1829 server
.findFirst
<KeychainDbGlobal
, const DbIdentifier
&>(&KeychainDbGlobal::identifier
, identifier())) {
1831 secinfo("KCdb", "%p linking to existing DbGlobal %p", this, dbglobal
);
1833 // DbGlobal not present; make a new one
1834 parent(*new KeychainDbGlobal(identifier()));
1835 secinfo("KCdb", "%p linking to new DbGlobal %p", this, &global());
1838 // link lifetime to the Session
1839 session().addReference(*this);
1841 if (strcasestr(id
.dbName(), "login.keychain") != NULL
) {
1842 mLoginKeychain
= true;
1847 void KeychainDbCommon::initializeKeybag() {
1848 if (mLoginKeychain
&& !session().keybagGetState(session_keybag_loaded
)) {
1849 service_context_t context
= session().get_current_service_context();
1850 if (service_client_kb_load(&context
) == 0) {
1851 session().keybagSetState(session_keybag_loaded
);
1856 KeychainDbCommon::KeychainDbCommon(Session
&ssn
, const DbIdentifier
&id
, KeychainDbCommon
& toClone
)
1857 : LocalDbCommon(ssn
), DatabaseCryptoCore(toClone
.mBlobVersion
), sequence(toClone
.sequence
), mParams(toClone
.mParams
), version(toClone
.version
),
1858 mIdentifier(id
), mIsLocked(toClone
.mIsLocked
), mValidParams(toClone
.mValidParams
), mLoginKeychain(toClone
.mLoginKeychain
)
1863 Server
&server
= Server::active();
1864 StLock
<Mutex
> _(server
);
1865 if (KeychainDbGlobal
*dbglobal
=
1866 server
.findFirst
<KeychainDbGlobal
, const DbIdentifier
&>(&KeychainDbGlobal::identifier
, identifier())) {
1868 secinfo("KCdb", "%p linking to existing DbGlobal %p", this, dbglobal
);
1870 // DbGlobal not present; make a new one
1871 parent(*new KeychainDbGlobal(identifier()));
1872 secinfo("KCdb", "%p linking to new DbGlobal %p", this, &global());
1874 session().addReference(*this);
1878 KeychainDbCommon::~KeychainDbCommon()
1880 secinfo("KCdb", "releasing keychain %p %s", this, (char*)this->dbName());
1882 // explicitly unschedule ourselves
1883 Server::active().clearTimer(this);
1884 if (mLoginKeychain
) {
1885 session().keybagClearState(session_keybag_unlocked
);
1887 // remove ourselves from mCommonSet
1891 void KeychainDbCommon::cloneFrom(KeychainDbCommon
& toClone
, uint32 requestedVersion
) {
1892 // don't clone the mIdentifier
1893 sequence
= toClone
.sequence
;
1894 mParams
= toClone
.mParams
;
1895 version
= toClone
.version
;
1896 mIsLocked
= toClone
.mIsLocked
;
1897 mValidParams
= toClone
.mValidParams
;
1898 mLoginKeychain
= toClone
.mLoginKeychain
;
1900 DatabaseCryptoCore::initializeFrom(toClone
, requestedVersion
);
1903 void KeychainDbCommon::kill()
1905 StReadWriteLock
_(mRWCommonLock
, StReadWriteLock::Write
);
1906 mCommonSet
.erase(this);
1909 KeychainDbGlobal
&KeychainDbCommon::global() const
1911 return parent
<KeychainDbGlobal
>();
1915 void KeychainDbCommon::select()
1918 void KeychainDbCommon::unselect()
1923 void KeychainDbCommon::makeNewSecrets()
1925 // we already have a master key (right?)
1926 assert(hasMaster());
1928 // tell crypto core to generate the use keys
1929 DatabaseCryptoCore::generateNewSecrets();
1931 // we're now officially "unlocked"; set the timer
1937 // All unlocking activity ultimately funnels through this method.
1938 // This unlocks a DbCommon using the secrets setup in its crypto core
1939 // component, and performs all the housekeeping needed to represent
1940 // the state change.
1941 // Returns true if unlock was successful, false if it failed due to
1942 // invalid/insufficient secrets. Throws on other errors.
1944 bool KeychainDbCommon::unlockDb(DbBlob
*blob
, void **privateAclBlob
)
1947 // Tell the cryptocore to (try to) decode itself. This will fail
1948 // in an astonishing variety of ways if the passphrase is wrong.
1949 assert(hasMaster());
1950 decodeCore(blob
, privateAclBlob
);
1951 secinfo("KCdb", "%p unlock successful", this);
1953 secinfo("KCdb", "%p unlock failed", this);
1957 // get the database parameters only if we haven't got them yet
1958 if (!mValidParams
) {
1959 mParams
= blob
->params
;
1960 n2hi(mParams
.idleTimeout
);
1961 mValidParams
= true; // sticky
1964 bool isLocked
= mIsLocked
;
1966 setUnlocked(); // mark unlocked
1969 // broadcast unlock notification, but only if we were previously locked
1970 notify(kNotificationEventUnlocked
);
1971 secinfo("KCdb", "unlocking keychain %p %s", this, (char*)this->dbName());
1976 void KeychainDbCommon::setUnlocked()
1978 session().addReference(*this); // active/held
1979 mIsLocked
= false; // mark unlocked
1980 activity(); // set timeout timer
1984 void KeychainDbCommon::lockDb()
1987 StLock
<Mutex
> _(*this);
1989 DatabaseCryptoCore::invalidate();
1990 notify(kNotificationEventLocked
);
1991 secinfo("KCdb", "locking keychain %p %s", this, (char*)this->dbName());
1992 Server::active().clearTimer(this);
1994 mIsLocked
= true; // mark locked
1996 // this call may destroy us if we have no databases anymore
1997 session().removeReference(*this);
2003 DbBlob
*KeychainDbCommon::encode(KeychainDatabase
&db
)
2005 assert(!isLocked()); // must have been unlocked by caller
2007 // export database ACL to blob form
2008 CssmData pubAcl
, privAcl
;
2009 db
.acl().exportBlob(pubAcl
, privAcl
);
2011 // tell the cryptocore to form the blob
2013 form
.randomSignature
= identifier();
2014 form
.sequence
= sequence
;
2015 form
.params
= mParams
;
2016 h2ni(form
.params
.idleTimeout
);
2018 assert(hasMaster());
2019 DbBlob
*blob
= encodeCore(form
, pubAcl
, privAcl
);
2022 db
.acl().allocator
.free(pubAcl
);
2023 db
.acl().allocator
.free(privAcl
);
2029 // Perform deferred lock processing for a database.
2031 void KeychainDbCommon::action()
2033 secinfo("KCdb", "common %s(%p) locked by timer", dbName(), this);
2037 void KeychainDbCommon::activity()
2040 secinfo("KCdb", "setting DbCommon %p timer to %d",
2041 this, int(mParams
.idleTimeout
));
2042 Server::active().setTimer(this, Time::Interval(int(mParams
.idleTimeout
)));
2046 void KeychainDbCommon::sleepProcessing()
2048 secinfo("KCdb", "common %s(%p) sleep-lock processing", dbName(), this);
2049 if (mParams
.lockOnSleep
&& !isDefaultSystemKeychain()) {
2050 StLock
<Mutex
> _(*this);
2055 void KeychainDbCommon::lockProcessing()
2062 // We consider a keychain to belong to the system domain if it resides
2063 // in /Library/Keychains. That's not exactly fool-proof, but we don't
2064 // currently have any internal markers to interrogate.
2066 bool KeychainDbCommon::belongsToSystem() const
2068 if (const char *name
= this->dbName())
2069 return !strncmp(name
, "/Library/Keychains/", 19);
2073 bool KeychainDbCommon::isDefaultSystemKeychain() const
2075 // /Library/Keychains/System.keychain (34)
2076 if (const char *name
= this->dbName())
2077 return !strncmp(name
, "/Library/Keychains/System.keychain", 34);
2082 // Keychain global objects
2084 KeychainDbGlobal::KeychainDbGlobal(const DbIdentifier
&id
)
2089 KeychainDbGlobal::~KeychainDbGlobal()
2091 secinfo("KCdb", "DbGlobal %p destroyed", this);