]> git.saurik.com Git - apple/securityd.git/blob - src/kcdatabase.cpp
securityd-55199.3.tar.gz
[apple/securityd.git] / src / kcdatabase.cpp
1 /*
2 * Copyright (c) 2000-2008 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 //
26 // kcdatabase - software database container implementation.
27 //
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.
39 //
40 #include "kcdatabase.h"
41 #include "agentquery.h"
42 #include "kckey.h"
43 #include "server.h"
44 #include "session.h"
45 #include "notifications.h"
46 #include <vector> // @@@ 4003540 workaround
47 #include <security_agent_client/agentclient.h>
48 #include <security_cdsa_utilities/acl_any.h> // for default owner ACLs
49 #include <security_cdsa_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 "securityd_service/securityd_service/securityd_service_client.h"
58 #include <AssertMacros.h>
59 #include <syslog.h>
60
61 void unflattenKey(const CssmData &flatKey, CssmKey &rawKey); //>> make static method on KeychainDatabase
62
63 static int
64 unlock_keybag(KeychainDbCommon & dbCommon, const void * secret, int secret_len)
65 {
66 int rc = -1;
67
68 if (!dbCommon.isLoginKeychain()) return 0;
69
70 service_context_t context = dbCommon.session().get_current_service_context();
71
72 // try to unlock first if not found then load/create or unlock
73 // loading should happen when the kb common object is created
74 // if it doesn't exist yet then the unlock will fail and we'll create everything
75 rc = service_client_kb_unlock(&context, secret, secret_len);
76 if (rc == KB_BagNotLoaded) {
77 if (service_client_kb_load(&context) == KB_BagNotFound) {
78 rc = service_client_kb_create(&context, secret, secret_len);
79 } else {
80 rc = service_client_kb_unlock(&context, secret, secret_len);
81 }
82 }
83
84 if (rc != 0) { // if we just upgraded make sure we swap the encryption key to the password
85 if (!dbCommon.session().keybagGetState(session_keybag_check_master_key)) {
86 CssmAutoData encKey(Allocator::standard(Allocator::sensitive));
87 dbCommon.get_encryption_key(encKey);
88 if ((rc = service_client_kb_unlock(&context, encKey.data(), (int)encKey.length())) == 0) {
89 rc = service_client_kb_change_secret(&context, encKey.data(), (int)encKey.length(), secret, secret_len);
90 }
91
92 if (rc != 0) {
93 CssmAutoData masterKey(Allocator::standard(Allocator::sensitive));
94 masterKey = dbCommon.masterKey()->keyData();
95 if ((rc = service_client_kb_unlock(&context, masterKey.data(), (int)masterKey.length())) == 0) {
96 rc = service_client_kb_change_secret(&context, masterKey.data(), (int)masterKey.length(), secret, secret_len);
97 }
98 }
99
100 if (rc != 0) { // if a login.keychain password exists but doesnt on the keybag update it
101 bool no_pin = false;
102 if ((secret_len > 0) && service_client_kb_is_locked(&context, NULL, &no_pin) == 0) {
103 if (no_pin) {
104 syslog(LOG_ERR, "Updating iCloud keychain passphrase for uid %d", dbCommon.session().originatorUid());
105 service_client_kb_change_secret(&context, NULL, 0, secret, secret_len);
106 }
107 }
108 }
109 } // session_keybag_check_master_key
110 }
111
112 if (rc == 0) {
113 dbCommon.session().keybagSetState(session_keybag_unlocked|session_keybag_loaded|session_keybag_check_master_key);
114 } else {
115 syslog(LOG_ERR, "Failed to unlock iCloud keychain for uid %d", dbCommon.session().originatorUid());
116 }
117
118 return rc;
119 }
120
121 static void
122 change_secret_on_keybag(KeychainDbCommon & dbCommon, const void * secret, int secret_len, const void * new_secret, int new_secret_len)
123 {
124 if (!dbCommon.isLoginKeychain()) return;
125
126 service_context_t context = dbCommon.session().get_current_service_context();
127
128 // if a login.keychain doesn't exist yet it comes into securityd as a create then change_secret
129 // we need to create the keybag in this case if it doesn't exist
130 if (service_client_kb_change_secret(&context, secret, secret_len, new_secret, new_secret_len) == KB_BagNotLoaded) {
131 if (service_client_kb_load(&context) == KB_BagNotFound) {
132 service_client_kb_create(&context, new_secret, new_secret_len);
133 }
134 }
135 }
136
137 //
138 // Create a Database object from initial parameters (create operation)
139 //
140 KeychainDatabase::KeychainDatabase(const DLDbIdentifier &id, const DBParameters &params, Process &proc,
141 const AccessCredentials *cred, const AclEntryPrototype *owner)
142 : LocalDatabase(proc), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive)), mSaveSecret(false), version(0), mBlob(NULL)
143 {
144 // save a copy of the credentials for later access control
145 mCred = DataWalkers::copy(cred, Allocator::standard());
146
147 // create a new random signature to complete the DLDbIdentifier
148 DbBlob::Signature newSig;
149 Server::active().random(newSig.bytes);
150 DbIdentifier ident(id, newSig);
151
152 // create common block and initialize
153 RefPointer<KeychainDbCommon> newCommon = new KeychainDbCommon(proc.session(), ident);
154 StLock<Mutex> _(*newCommon);
155 parent(*newCommon);
156 // new common is now visible (in ident-map) but we hold its lock
157
158 // establish the new master secret
159 establishNewSecrets(cred, SecurityAgent::newDatabase);
160
161 // set initial database parameters
162 common().mParams = params;
163
164 // the common is "unlocked" now
165 common().makeNewSecrets();
166
167 // establish initial ACL
168 if (owner)
169 acl().cssmSetInitial(*owner);
170 else
171 acl().cssmSetInitial(new AnyAclSubject());
172 mValidData = true;
173
174 // for now, create the blob immediately
175 encode();
176
177 proc.addReference(*this);
178
179 // this new keychain is unlocked; make it so
180 activity();
181
182 SECURITYD_KEYCHAIN_CREATE(&common(), (char*)this->dbName(), this);
183 }
184
185
186 //
187 // Create a Database object from a database blob (decoding)
188 //
189 KeychainDatabase::KeychainDatabase(const DLDbIdentifier &id, const DbBlob *blob, Process &proc,
190 const AccessCredentials *cred)
191 : LocalDatabase(proc), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive)), mSaveSecret(false), version(0), mBlob(NULL)
192 {
193 validateBlob(blob);
194
195 // save a copy of the credentials for later access control
196 mCred = DataWalkers::copy(cred, Allocator::standard());
197 mBlob = blob->copy();
198
199 // check to see if we already know about this database
200 DbIdentifier ident(id, blob->randomSignature);
201 Session &session = process().session();
202 if (RefPointer<KeychainDbCommon> dbcom =
203 session.findFirst<KeychainDbCommon, const DbIdentifier &>(&KeychainDbCommon::identifier, ident)) {
204 parent(*dbcom);
205 //@@@ arbitrate sequence number here, perhaps update common().mParams
206 SECURITYD_KEYCHAIN_JOIN(&common(), (char*)this->dbName(), this);
207 } else {
208 // DbCommon not present; make a new one
209 parent(*new KeychainDbCommon(proc.session(), ident));
210 common().mParams = blob->params;
211 SECURITYD_KEYCHAIN_MAKE(&common(), (char*)this->dbName(), this);
212 // this DbCommon is locked; no timer or reference setting
213 }
214 proc.addReference(*this);
215 }
216
217
218 // recode/clone:
219 //
220 // Special-purpose constructor for keychain synchronization. Copies an
221 // existing keychain but uses the operational keys from secretsBlob. The
222 // new KeychainDatabase will silently replace the existing KeychainDatabase
223 // as soon as the client declares that re-encoding of all keychain items is
224 // finished. This is a little perilous since it allows a client to dictate
225 // securityd state, but we try to ensure that only the client that started
226 // the re-encoding can declare it done.
227 //
228 KeychainDatabase::KeychainDatabase(KeychainDatabase &src, Process &proc, DbHandle dbToClone)
229 : LocalDatabase(proc), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive)), mSaveSecret(false), version(0), mBlob(NULL)
230 {
231 mCred = DataWalkers::copy(src.mCred, Allocator::standard());
232
233 // Give this KeychainDatabase a temporary name
234 std::string newDbName = std::string("////") + std::string(src.identifier().dbName());
235 DLDbIdentifier newDLDbIdent(src.identifier().dlDbIdentifier().ssuid(), newDbName.c_str(), src.identifier().dlDbIdentifier().dbLocation());
236 DbIdentifier ident(newDLDbIdent, src.identifier());
237
238 // create common block and initialize
239 RefPointer<KeychainDbCommon> newCommon = new KeychainDbCommon(proc.session(), ident);
240 StLock<Mutex> _(*newCommon);
241 parent(*newCommon);
242
243 // set initial database parameters from the source keychain
244 common().mParams = src.common().mParams;
245
246 // establish the source keychain's master secret as ours
247 // @@@ NB: this is a v. 0.1 assumption. We *should* trigger new UI
248 // that offers the user the option of using the existing password
249 // or choosing a new one. That would require a new
250 // SecurityAgentQuery type, new UI, and--possibly--modifications to
251 // ensure that the new password is available here to generate the
252 // new master secret.
253 src.unlockDb(); // precaution for masterKey()
254 common().setup(src.blob(), src.common().masterKey());
255
256 // import the operational secrets
257 RefPointer<KeychainDatabase> srcKC = Server::keychain(dbToClone);
258 common().importSecrets(srcKC->common());
259
260 // import source keychain's ACL
261 CssmData pubAcl, privAcl;
262 src.acl().exportBlob(pubAcl, privAcl);
263 importBlob(pubAcl.data(), privAcl.data());
264 src.acl().allocator.free(pubAcl);
265 src.acl().allocator.free(privAcl);
266
267 // indicate that this keychain should be allowed to do some otherwise
268 // risky things required for copying, like re-encoding keys
269 mRecodingSource = &src;
270
271 common().setUnlocked();
272 mValidData = true;
273
274 encode();
275
276 proc.addReference(*this);
277 secdebug("SSdb", "database %s(%p) created as copy, common at %p",
278 common().dbName(), this, &common());
279 }
280
281 //
282 // Destroy a Database
283 //
284 KeychainDatabase::~KeychainDatabase()
285 {
286 secdebug("KCdb", "deleting database %s(%p) common %p",
287 common().dbName(), this, &common());
288 Allocator::standard().free(mCred);
289 Allocator::standard().free(mBlob);
290 }
291
292
293 //
294 // Basic Database virtual implementations
295 //
296 KeychainDbCommon &KeychainDatabase::common() const
297 {
298 return parent<KeychainDbCommon>();
299 }
300
301 const char *KeychainDatabase::dbName() const
302 {
303 return common().dbName();
304 }
305
306 bool KeychainDatabase::transient() const
307 {
308 return false; // has permanent store
309 }
310
311 AclKind KeychainDatabase::aclKind() const
312 {
313 return dbAcl;
314 }
315
316 Database *KeychainDatabase::relatedDatabase()
317 {
318 return this;
319 }
320
321
322 static inline KeychainKey &myKey(Key *key)
323 {
324 return *safe_cast<KeychainKey *>(key);
325 }
326
327
328 //
329 // (Re-)Authenticate the database. This changes the stored credentials.
330 //
331 void KeychainDatabase::authenticate(CSSM_DB_ACCESS_TYPE mode,
332 const AccessCredentials *cred)
333 {
334 StLock<Mutex> _(common());
335
336 // the (Apple specific) RESET bit means "lock the database now"
337 switch (mode) {
338 case CSSM_DB_ACCESS_RESET:
339 secdebug("KCdb", "%p ACCESS_RESET triggers keychain lock", this);
340 common().lockDb();
341 break;
342 default:
343 // store the new credentials for future use
344 secdebug("KCdb", "%p authenticate stores new database credentials", this);
345 AccessCredentials *newCred = DataWalkers::copy(cred, Allocator::standard());
346 Allocator::standard().free(mCred);
347 mCred = newCred;
348 }
349 }
350
351
352 //
353 // Make a new KeychainKey.
354 // If PERMANENT is off, make a temporary key instead.
355 // The db argument allows you to create for another KeychainDatabase (only);
356 // it defaults to ourselves.
357 //
358 RefPointer<Key> KeychainDatabase::makeKey(Database &db, const CssmKey &newKey,
359 uint32 moreAttributes, const AclEntryPrototype *owner)
360 {
361
362 if (moreAttributes & CSSM_KEYATTR_PERMANENT)
363 return new KeychainKey(db, newKey, moreAttributes, owner);
364 else
365 return process().makeTemporaryKey(newKey, moreAttributes, owner);
366 }
367
368 RefPointer<Key> KeychainDatabase::makeKey(const CssmKey &newKey,
369 uint32 moreAttributes, const AclEntryPrototype *owner)
370 {
371 return makeKey(*this, newKey, moreAttributes, owner);
372 }
373
374
375 //
376 // Return the database blob, recalculating it as needed.
377 //
378 DbBlob *KeychainDatabase::blob()
379 {
380 StLock<Mutex> _(common());
381 if (!validBlob()) {
382 makeUnlocked(); // unlock to get master secret
383 encode(); // (re)encode blob if needed
384 }
385 activity(); // reset timeout
386 assert(validBlob()); // better have a valid blob now...
387 return mBlob;
388 }
389
390
391 //
392 // Encode the current database as a blob.
393 // Note that this returns memory we own and keep.
394 // Caller must hold common lock.
395 //
396 void KeychainDatabase::encode()
397 {
398 DbBlob *blob = common().encode(*this);
399 Allocator::standard().free(mBlob);
400 mBlob = blob;
401 version = common().version;
402 secdebug("KCdb", "encoded database %p common %p(%s) version %u params=(%u,%u)",
403 this, &common(), dbName(), version,
404 common().mParams.idleTimeout, common().mParams.lockOnSleep);
405 }
406
407
408 //
409 // Change the passphrase on a database
410 //
411 void KeychainDatabase::changePassphrase(const AccessCredentials *cred)
412 {
413 // get and hold the common lock (don't let other threads break in here)
414 StLock<Mutex> _(common());
415
416 // establish OLD secret - i.e. unlock the database
417 //@@@ do we want to leave the final lock state alone?
418 if (common().isLoginKeychain()) mSaveSecret = true;
419 makeUnlocked(cred);
420
421 // establish NEW secret
422 establishNewSecrets(cred, SecurityAgent::changePassphrase);
423 if (mSecret) { mSecret.reset(); }
424 mSaveSecret = false;
425 common().invalidateBlob(); // blob state changed
426 secdebug("KCdb", "Database %s(%p) master secret changed", common().dbName(), this);
427 encode(); // force rebuild of local blob
428
429 // send out a notification
430 notify(kNotificationEventPassphraseChanged);
431
432 // I guess this counts as an activity
433 activity();
434 }
435
436 //
437 // Second stage of keychain synchronization: overwrite the original keychain's
438 // (this KeychainDatabase's) operational secrets
439 //
440 void KeychainDatabase::commitSecretsForSync(KeychainDatabase &cloneDb)
441 {
442 StLock<Mutex> _(common());
443
444 // try to detect spoofing
445 if (cloneDb.mRecodingSource != this)
446 CssmError::throwMe(CSSM_ERRCODE_INVALID_DB_HANDLE);
447
448 // in case we autolocked since starting the sync
449 makeUnlocked(); // call this because we already own the lock
450 cloneDb.unlockDb(); // we may not own the lock here, so calling unlockDb will lock the cloneDb's common lock
451
452 // Decode all keys whose handles refer to this on-disk keychain so that
453 // if the holding client commits the key back to disk, it's encoded with
454 // the new operational secrets. The recoding client *must* hold a write
455 // lock for the on-disk keychain from the moment it starts recoding key
456 // items until after this call.
457 //
458 // @@@ This specific implementation is a workaround for 4003540.
459 std::vector<U32HandleObject::Handle> handleList;
460 U32HandleObject::findAllRefs<KeychainKey>(handleList);
461 size_t count = handleList.size();
462 if (count > 0) {
463 for (unsigned int n = 0; n < count; ++n) {
464 RefPointer<KeychainKey> kckey =
465 U32HandleObject::findRefAndLock<KeychainKey>(handleList[n], CSSMERR_CSP_INVALID_KEY_REFERENCE);
466 StLock<Mutex> _(*kckey/*, true*/);
467 if (kckey->database().global().identifier() == identifier()) {
468 kckey->key(); // force decode
469 kckey->invalidateBlob();
470 secdebug("kcrecode", "changed extant key %p (proc %d)",
471 &*kckey, kckey->process().pid());
472 }
473 }
474 }
475
476 // it is now safe to replace the old op secrets
477 common().importSecrets(cloneDb.common());
478 common().invalidateBlob();
479 }
480
481
482 //
483 // Extract the database master key as a proper Key object.
484 //
485 RefPointer<Key> KeychainDatabase::extractMasterKey(Database &db,
486 const AccessCredentials *cred, const AclEntryPrototype *owner,
487 uint32 usage, uint32 attrs)
488 {
489 // get and hold common lock
490 StLock<Mutex> _(common());
491
492 // force lock to require re-validation of credentials
493 lockDb();
494
495 // unlock to establish master secret
496 makeUnlocked();
497
498 // extract the raw cryptographic key
499 CssmClient::WrapKey wrap(Server::csp(), CSSM_ALGID_NONE);
500 CssmKey key;
501 wrap(common().masterKey(), key);
502
503 // make the key object and return it
504 return makeKey(db, key, attrs & LocalKey::managedAttributes, owner);
505 }
506
507
508 //
509 // Unlock this database (if needed) by obtaining the master secret in some
510 // suitable way and then proceeding to unlock with it.
511 // Does absolutely nothing if the database is already unlocked.
512 // The makeUnlocked forms are identical except the assume the caller already
513 // holds the common lock.
514 //
515 void KeychainDatabase::unlockDb()
516 {
517 StLock<Mutex> _(common());
518 makeUnlocked();
519 }
520
521 void KeychainDatabase::makeUnlocked()
522 {
523 return makeUnlocked(mCred);
524 }
525
526 void KeychainDatabase::makeUnlocked(const AccessCredentials *cred)
527 {
528 if (isLocked()) {
529 secdebug("KCdb", "%p(%p) unlocking for makeUnlocked()", this, &common());
530 assert(mBlob || (mValidData && common().hasMaster()));
531 establishOldSecrets(cred);
532 common().setUnlocked(); // mark unlocked
533 } else if (common().isLoginKeychain()) {
534 bool locked = false;
535 service_context_t context = common().session().get_current_service_context();
536 if ((service_client_kb_is_locked(&context, &locked, NULL) == 0) && locked) {
537 StSyncLock<Mutex, Mutex> uisync(common().uiLock(), common());
538 QueryKeybagPassphrase keybagQuery(common().session(), 3);
539 keybagQuery.inferHints(Server::process());
540 if (keybagQuery.query() != SecurityAgent::noReason) {
541 syslog(LOG_NOTICE, "failed to unlock iCloud keychain");
542 }
543 }
544 }
545 if (!mValidData) { // need to decode to get our ACLs, master secret available
546 secdebug("KCdb", "%p(%p) is unlocked; decoding for makeUnlocked()", this, &common());
547 if (!decode())
548 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
549 }
550 assert(!isLocked());
551 assert(mValidData);
552 }
553
554 //
555 // Invoke the securityd_service to retrieve the keychain master
556 // key from the AppleFDEKeyStore.
557 //
558 void KeychainDatabase::stashDbCheck()
559 {
560 CssmAutoData masterKey(Allocator::standard(Allocator::sensitive));
561 CssmAutoData encKey(Allocator::standard(Allocator::sensitive));
562
563 // Fetch the key
564 int rc = 0;
565 void * stash_key = NULL;
566 int stash_key_len = 0;
567 service_context_t context = common().session().get_current_service_context();
568 rc = service_client_stash_get_key(&context, &stash_key, &stash_key_len);
569 if (rc == 0) {
570 if (stash_key) {
571 masterKey.copy(CssmData((void *)stash_key,stash_key_len));
572 memset(stash_key, 0, stash_key_len);
573 free(stash_key);
574 }
575 } else {
576 CssmError::throwMe(rc);
577 }
578
579 {
580 StLock<Mutex> _(common());
581
582 // Now establish it as the keychain master key
583 CssmClient::Key key(Server::csp(), masterKey.get());
584 CssmKey::Header &hdr = key.header();
585 hdr.keyClass(CSSM_KEYCLASS_SESSION_KEY);
586 hdr.algorithm(CSSM_ALGID_3DES_3KEY_EDE);
587 hdr.usage(CSSM_KEYUSE_ANY);
588 hdr.blobType(CSSM_KEYBLOB_RAW);
589 hdr.blobFormat(CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING);
590 common().setup(mBlob, key);
591
592 if (!decode())
593 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
594
595 common().get_encryption_key(encKey);
596 }
597
598 // when upgrading from pre-10.9 create a keybag if it doesn't exist with the encryption key
599 // only do this after we have verified the master key unlocks the login.keychain
600 if (service_client_kb_load(&context) == KB_BagNotFound) {
601 service_client_kb_create(&context, encKey.data(), (int)encKey.length());
602 }
603 }
604
605 //
606 // Get the keychain master key and invoke the securityd_service
607 // to stash it in the AppleFDEKeyStore ready for commit to the
608 // NVRAM blob.
609 //
610 void KeychainDatabase::stashDb()
611 {
612 CssmAutoData data(Allocator::standard(Allocator::sensitive));
613
614 {
615 StLock<Mutex> _(common());
616
617 if (!common().isValid()) {
618 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
619 }
620
621 CssmKey key = common().masterKey();
622 data.copy(key.keyData());
623 }
624
625 service_context_t context = common().session().get_current_service_context();
626 int rc = service_client_stash_set_key(&context, data.data(), (int)data.length());
627 if (rc != 0) CssmError::throwMe(rc);
628 }
629
630 //
631 // The following unlock given an explicit passphrase, rather than using
632 // (special cred sample based) default procedures.
633 //
634 void KeychainDatabase::unlockDb(const CssmData &passphrase)
635 {
636 StLock<Mutex> _(common());
637 makeUnlocked(passphrase);
638 }
639
640 void KeychainDatabase::makeUnlocked(const CssmData &passphrase)
641 {
642 if (isLocked()) {
643 if (decode(passphrase))
644 return;
645 else
646 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
647 } else if (!mValidData) { // need to decode to get our ACLs, passphrase available
648 if (!decode())
649 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
650 }
651
652 if (common().isLoginKeychain()) {
653 bool locked = false;
654 service_context_t context = common().session().get_current_service_context();
655 if (!common().session().keybagGetState(session_keybag_check_master_key) || ((service_client_kb_is_locked(&context, &locked, NULL) == 0) && locked)) {
656 unlock_keybag(common(), passphrase.data(), (int)passphrase.length());
657 }
658 }
659
660 assert(!isLocked());
661 assert(mValidData);
662 }
663
664
665 //
666 // Nonthrowing passphrase-based unlock. This returns false if unlock failed.
667 // Note that this requires an explicitly given passphrase.
668 // Caller must hold common lock.
669 //
670 bool KeychainDatabase::decode(const CssmData &passphrase)
671 {
672 assert(mBlob);
673 common().setup(mBlob, passphrase);
674 bool success = decode();
675 if (success && common().isLoginKeychain()) {
676 unlock_keybag(common(), passphrase.data(), (int)passphrase.length());
677 }
678 return success;
679 }
680
681
682 //
683 // Given the established master secret, decode the working keys and other
684 // functional secrets for this database. Return false (do NOT throw) if
685 // the decode fails. Call this in low(er) level code once you established
686 // the master key.
687 //
688 bool KeychainDatabase::decode()
689 {
690 assert(mBlob);
691 assert(common().hasMaster());
692 void *privateAclBlob;
693 if (common().unlockDb(mBlob, &privateAclBlob)) {
694 if (!mValidData) {
695 acl().importBlob(mBlob->publicAclBlob(), privateAclBlob);
696 mValidData = true;
697 }
698 Allocator::standard().free(privateAclBlob);
699 return true;
700 }
701 secdebug("KCdb", "%p decode failed", this);
702 return false;
703 }
704
705
706 //
707 // Given an AccessCredentials for this database, wring out the existing primary
708 // database secret by whatever means necessary.
709 // On entry, caller must hold the database common lock. It will be held
710 // throughout except when user interaction is required. User interaction
711 // requires relinquishing the database common lock and taking the UI lock. On
712 // return from user interaction, the UI lock is relinquished and the database
713 // common lock must be reacquired. At no time may the caller hold both locks.
714 // On exit, the crypto core has its master secret. If things go wrong,
715 // we will throw a suitable exception. Note that encountering any malformed
716 // credential sample will throw, but this is not guaranteed -- don't assume
717 // that NOT throwing means creds is entirely well-formed (it may just be good
718 // enough to work THIS time).
719 //
720 // How this works:
721 // Walk through the creds. Fish out those credentials (in order) that
722 // are for unlock processing (they have no ACL subject correspondents),
723 // and (try to) obey each in turn, until one produces a valid secret
724 // or you run out. If no special samples are found at all, interpret that as
725 // "use the system global default," which happens to be hard-coded right here.
726 //
727 void KeychainDatabase::establishOldSecrets(const AccessCredentials *creds)
728 {
729 bool forSystem = this->belongsToSystem(); // this keychain belongs to the system security domain
730
731 // attempt system-keychain unlock
732 if (forSystem) {
733 SystemKeychainKey systemKeychain(kSystemUnlockFile);
734 if (systemKeychain.matches(mBlob->randomSignature)) {
735 secdebug("KCdb", "%p attempting system unlock", this);
736 common().setup(mBlob, CssmClient::Key(Server::csp(), systemKeychain.key(), true));
737 if (decode())
738 return;
739 }
740 }
741
742 list<CssmSample> samples;
743 if (creds && creds->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, samples)) {
744 for (list<CssmSample>::iterator it = samples.begin(); it != samples.end(); it++) {
745 TypedList &sample = *it;
746 sample.checkProper();
747 switch (sample.type()) {
748 // interactively prompt the user - no additional data
749 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT:
750 if (!forSystem) {
751 if (interactiveUnlock())
752 return;
753 }
754 break;
755 // try to use an explicitly given passphrase - Data:passphrase
756 case CSSM_SAMPLE_TYPE_PASSWORD:
757 if (sample.length() != 2)
758 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
759 secdebug("KCdb", "%p attempting passphrase unlock", this);
760 if (decode(sample[1]))
761 return;
762 break;
763 // try to open with a given master key - Data:CSP or KeyHandle, Data:CssmKey
764 case CSSM_SAMPLE_TYPE_SYMMETRIC_KEY:
765 case CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY:
766 assert(mBlob);
767 secdebug("KCdb", "%p attempting explicit key unlock", this);
768 common().setup(mBlob, keyFromCreds(sample, 4));
769 if (decode())
770 return;
771 break;
772 // explicitly defeat the default action but don't try anything in particular
773 case CSSM_WORDID_CANCELED:
774 secdebug("KCdb", "%p defeat default action", this);
775 break;
776 default:
777 // Unknown sub-sample for unlocking.
778 // If we wanted to be fascist, we could now do
779 // CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
780 // But instead we try to be tolerant and continue on.
781 // This DOES however count as an explicit attempt at specifying unlock,
782 // so we will no longer try the default case below...
783 secdebug("KCdb", "%p unknown sub-sample unlock (%d) ignored", this, sample.type());
784 break;
785 }
786 }
787 } else {
788 // default action
789 assert(mBlob);
790
791 if (!forSystem) {
792 if (interactiveUnlock())
793 return;
794 }
795 }
796
797 // out of options - no secret obtained
798 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
799 }
800
801 bool KeychainDatabase::interactiveUnlock()
802 {
803 secdebug("KCdb", "%p attempting interactive unlock", this);
804 SecurityAgent::Reason reason = SecurityAgent::noReason;
805 QueryUnlock query(*this);
806 // take UI interlock and release DbCommon lock (to avoid deadlocks)
807 StSyncLock<Mutex, Mutex> uisync(common().uiLock(), common());
808
809 // now that we have the UI lock, interact unless another thread unlocked us first
810 if (isLocked()) {
811 query.inferHints(Server::process());
812 reason = query();
813 if (mSaveSecret && reason == SecurityAgent::noReason) {
814 query.retrievePassword(mSecret);
815 }
816 query.disconnect();
817 } else {
818 secdebug("KCdb", "%p was unlocked during uiLock delay", this);
819 }
820
821 if (common().isLoginKeychain()) {
822 bool locked = false;
823 service_context_t context = common().session().get_current_service_context();
824 if ((service_client_kb_is_locked(&context, &locked, NULL) == 0) && locked) {
825 QueryKeybagNewPassphrase keybagQuery(common().session());
826 keybagQuery.inferHints(Server::process());
827 CssmAutoData pass(Allocator::standard(Allocator::sensitive));
828 CssmAutoData oldPass(Allocator::standard(Allocator::sensitive));
829 SecurityAgent::Reason queryReason = keybagQuery.query(oldPass, pass);
830 if (queryReason == SecurityAgent::noReason) {
831 service_client_kb_change_secret(&context, oldPass.data(), (int)oldPass.length(), pass.data(), (int)pass.length());
832 } else if (queryReason == SecurityAgent::resettingPassword) {
833 query.retrievePassword(pass);
834 service_client_kb_reset(&context, pass.data(), (int)pass.length());
835 }
836
837 }
838 }
839
840 return reason == SecurityAgent::noReason;
841 }
842
843
844 //
845 // Same thing, but obtain a new secret somehow and set it into the common.
846 //
847 void KeychainDatabase::establishNewSecrets(const AccessCredentials *creds, SecurityAgent::Reason reason)
848 {
849 list<CssmSample> samples;
850 if (creds && creds->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK, samples)) {
851 for (list<CssmSample>::iterator it = samples.begin(); it != samples.end(); it++) {
852 TypedList &sample = *it;
853 sample.checkProper();
854 switch (sample.type()) {
855 // interactively prompt the user
856 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT:
857 {
858 secdebug("KCdb", "%p specified interactive passphrase", this);
859 QueryNewPassphrase query(*this, reason);
860 StSyncLock<Mutex, Mutex> uisync(common().uiLock(), common());
861 query.inferHints(Server::process());
862 CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
863 CssmAutoData oldPassphrase(Allocator::standard(Allocator::sensitive));
864 if (query(oldPassphrase, passphrase) == SecurityAgent::noReason) {
865 common().setup(NULL, passphrase);
866 change_secret_on_keybag(common(), oldPassphrase.data(), (int)oldPassphrase.length(), passphrase.data(), (int)passphrase.length());
867 return;
868 }
869 }
870 break;
871 // try to use an explicitly given passphrase
872 case CSSM_SAMPLE_TYPE_PASSWORD:
873 {
874 secdebug("KCdb", "%p specified explicit passphrase", this);
875 if (sample.length() != 2)
876 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
877 common().setup(NULL, sample[1]);
878 if (common().isLoginKeychain()) {
879 CssmAutoData oldPassphrase(Allocator::standard(Allocator::sensitive));
880 list<CssmSample> oldSamples;
881 creds->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, oldSamples);
882 for (list<CssmSample>::iterator oit = oldSamples.begin(); oit != oldSamples.end(); oit++) {
883 TypedList &tmpList = *oit;
884 tmpList.checkProper();
885 if (tmpList.type() == CSSM_SAMPLE_TYPE_PASSWORD) {
886 if (tmpList.length() == 2) {
887 oldPassphrase = tmpList[1].data();
888 }
889 }
890 }
891 if (!oldPassphrase.length() && mSecret && mSecret.length()) {
892 oldPassphrase = mSecret;
893 }
894 change_secret_on_keybag(common(), oldPassphrase.data(), (int)oldPassphrase.length(), sample[1].data().data(), (int)sample[1].data().length());
895 }
896 return;
897 }
898 // try to open with a given master key
899 case CSSM_WORDID_SYMMETRIC_KEY:
900 case CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY:
901 secdebug("KCdb", "%p specified explicit master key", this);
902 common().setup(NULL, keyFromCreds(sample, 3));
903 return;
904 // explicitly defeat the default action but don't try anything in particular
905 case CSSM_WORDID_CANCELED:
906 secdebug("KCdb", "%p defeat default action", this);
907 break;
908 default:
909 // Unknown sub-sample for acquiring new secret.
910 // If we wanted to be fascist, we could now do
911 // CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
912 // But instead we try to be tolerant and continue on.
913 // This DOES however count as an explicit attempt at specifying unlock,
914 // so we will no longer try the default case below...
915 secdebug("KCdb", "%p unknown sub-sample acquisition (%d) ignored",
916 this, sample.type());
917 break;
918 }
919 }
920 } else {
921 // default action -- interactive (only)
922 QueryNewPassphrase query(*this, reason);
923 StSyncLock<Mutex, Mutex> uisync(common().uiLock(), common());
924 query.inferHints(Server::process());
925 CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
926 CssmAutoData oldPassphrase(Allocator::standard(Allocator::sensitive));
927 if (query(oldPassphrase, passphrase) == SecurityAgent::noReason) {
928 common().setup(NULL, passphrase);
929 change_secret_on_keybag(common(), oldPassphrase.data(), (int)oldPassphrase.length(), passphrase.data(), (int)passphrase.length());
930 return;
931 }
932 }
933
934 // out of options - no secret obtained
935 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
936 }
937
938
939 //
940 // Given a (truncated) Database credentials TypedList specifying a master key,
941 // locate the key and return a reference to it.
942 //
943 CssmClient::Key KeychainDatabase::keyFromCreds(const TypedList &sample, unsigned int requiredLength)
944 {
945 // decode TypedList structure (sample type; Data:CSPHandle; Data:CSSM_KEY)
946 assert(sample.type() == CSSM_SAMPLE_TYPE_SYMMETRIC_KEY || sample.type() == CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY);
947 if (sample.length() != requiredLength
948 || sample[1].type() != CSSM_LIST_ELEMENT_DATUM
949 || sample[2].type() != CSSM_LIST_ELEMENT_DATUM
950 || (requiredLength == 4 && sample[3].type() != CSSM_LIST_ELEMENT_DATUM))
951 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
952 KeyHandle &handle = *sample[1].data().interpretedAs<KeyHandle>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
953 // We used to be able to check the length but supporting multiple client
954 // architectures dishes that (sizeof(CSSM_KEY) varies due to alignment and
955 // field-size differences). The decoding in the transition layer should
956 // serve as a sufficient garbling check anyway.
957 if (sample[2].data().data() == NULL)
958 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
959 CssmKey &key = *sample[2].data().interpretedAs<CssmKey>();
960
961 if (key.header().cspGuid() == gGuidAppleCSPDL) {
962 // handleOrKey is a SecurityServer KeyHandle; ignore key argument
963 return safer_cast<LocalKey &>(*Server::key(handle));
964 } else
965 if (sample.type() == CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY) {
966 /*
967 Contents (see DefaultCredentials::unlockKey in libsecurity_keychain/defaultcreds.cpp)
968
969 sample[0] sample type
970 sample[1] csp handle for master or wrapping key; is really a keyhandle
971 sample[2] masterKey [not used since securityd cannot interpret; use sample[1] handle instead]
972 sample[3] UnlockReferralRecord data, in this case the flattened symmetric key
973 */
974
975 // RefPointer<Key> Server::key(KeyHandle key)
976 KeyHandle keyhandle = *sample[1].data().interpretedAs<KeyHandle>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
977 CssmData &flattenedKey = sample[3].data();
978 RefPointer<Key> unwrappingKey = Server::key(keyhandle);
979 Database &db=unwrappingKey->database();
980
981 CssmKey rawWrappedKey;
982 unflattenKey(flattenedKey, rawWrappedKey);
983
984 RefPointer<Key> masterKey;
985 CssmData emptyDescriptiveData;
986 const AccessCredentials *cred = NULL;
987 const AclEntryPrototype *owner = NULL;
988 CSSM_KEYUSE usage = CSSM_KEYUSE_ANY;
989 CSSM_KEYATTR_FLAGS attrs = CSSM_KEYATTR_EXTRACTABLE; //CSSM_KEYATTR_RETURN_REF |
990
991 // Get default credentials for unwrappingKey (the one on the token)
992 // Copied from Statics::Statics() in libsecurity_keychain/aclclient.cpp
993 // Following KeyItem::getCredentials, one sees that the "operation" parameter
994 // e.g. "CSSM_ACL_AUTHORIZATION_DECRYPT" is ignored
995 Allocator &alloc = Allocator::standard();
996 AutoCredentials promptCred(alloc, 3);// enable interactive prompting
997
998 // promptCred: a credential permitting user prompt confirmations
999 // contains:
1000 // a KEYCHAIN_PROMPT sample, both by itself and in a THRESHOLD
1001 // a PROMPTED_PASSWORD sample
1002 promptCred.sample(0) = TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT);
1003 promptCred.sample(1) = TypedList(alloc, CSSM_SAMPLE_TYPE_THRESHOLD,
1004 new(alloc) ListElement(TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT)));
1005 promptCred.sample(2) = TypedList(alloc, CSSM_SAMPLE_TYPE_PROMPTED_PASSWORD,
1006 new(alloc) ListElement(alloc, CssmData()));
1007
1008 // This unwrap object is here just to provide a context
1009 CssmClient::UnwrapKey unwrap(Server::csp(), CSSM_ALGID_NONE); //ok to lie about csp here
1010 unwrap.mode(CSSM_ALGMODE_NONE);
1011 unwrap.padding(CSSM_PADDING_PKCS1);
1012 unwrap.cred(promptCred);
1013 unwrap.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT, uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7));
1014 Security::Context *tmpContext;
1015 CSSM_CC_HANDLE CCHandle = unwrap.handle();
1016 /*CSSM_RETURN rx = */ CSSM_GetContext (CCHandle, (CSSM_CONTEXT_PTR *)&tmpContext);
1017
1018 // OK, this is skanky but necessary. We overwrite fields in the context struct
1019
1020 tmpContext->ContextType = CSSM_ALGCLASS_ASYMMETRIC;
1021 tmpContext->AlgorithmType = CSSM_ALGID_RSA;
1022
1023 db.unwrapKey(*tmpContext, cred, owner, unwrappingKey, NULL, usage, attrs,
1024 rawWrappedKey, masterKey, emptyDescriptiveData);
1025
1026 Allocator::standard().free(rawWrappedKey.KeyData.Data);
1027
1028 return safer_cast<LocalKey &>(*masterKey).key();
1029 }
1030 else
1031 {
1032 // not a KeyHandle reference; use key as a raw key
1033 if (key.header().blobType() != CSSM_KEYBLOB_RAW)
1034 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE);
1035 if (key.header().keyClass() != CSSM_KEYCLASS_SESSION_KEY)
1036 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
1037 return CssmClient::Key(Server::csp(), key, true);
1038 }
1039 }
1040
1041 void unflattenKey(const CssmData &flatKey, CssmKey &rawKey)
1042 {
1043 // unflatten the raw input key naively: key header then key data
1044 // We also convert it back to host byte order
1045 // A CSSM_KEY is a CSSM_KEYHEADER followed by a CSSM_DATA
1046
1047 // Now copy: header, then key struct, then key data
1048 memcpy(&rawKey.KeyHeader, flatKey.Data, sizeof(CSSM_KEYHEADER));
1049 memcpy(&rawKey.KeyData, flatKey.Data + sizeof(CSSM_KEYHEADER), sizeof(CSSM_DATA));
1050 const uint32 keyDataLength = flatKey.length() - sizeof(CSSM_KEY);
1051 rawKey.KeyData.Data = Allocator::standard().malloc<uint8>(keyDataLength);
1052 rawKey.KeyData.Length = keyDataLength;
1053 memcpy(rawKey.KeyData.Data, flatKey.Data + sizeof(CSSM_KEY), keyDataLength);
1054 Security::n2hi(rawKey.KeyHeader); // convert it to host byte order
1055 }
1056
1057
1058 //
1059 // Verify a putative database passphrase.
1060 // If the database is already unlocked, just check the passphrase.
1061 // Otherwise, unlock with that passphrase and report success.
1062 // Caller must hold the common lock.
1063 //
1064 bool KeychainDatabase::validatePassphrase(const CssmData &passphrase) const
1065 {
1066 if (common().hasMaster()) {
1067 // verify against known secret
1068 return common().validatePassphrase(passphrase);
1069 } else {
1070 // no master secret - perform "blind" unlock to avoid actual unlock
1071 try {
1072 DatabaseCryptoCore test;
1073 test.setup(mBlob, passphrase);
1074 test.decodeCore(mBlob, NULL);
1075 return true;
1076 } catch (...) {
1077 return false;
1078 }
1079 }
1080 }
1081
1082
1083 //
1084 // Lock this database
1085 //
1086 void KeychainDatabase::lockDb()
1087 {
1088 common().lockDb();
1089 }
1090
1091
1092 //
1093 // Given a Key for this database, encode it into a blob and return it.
1094 //
1095 KeyBlob *KeychainDatabase::encodeKey(const CssmKey &key, const CssmData &pubAcl, const CssmData &privAcl)
1096 {
1097 bool inTheClear = false;
1098 if((key.keyClass() == CSSM_KEYCLASS_PUBLIC_KEY) &&
1099 !(key.attribute(CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT))) {
1100 inTheClear = true;
1101 }
1102 StLock<Mutex> _(common());
1103 if(!inTheClear)
1104 makeUnlocked();
1105
1106 // tell the cryptocore to form the key blob
1107 return common().encodeKeyCore(key, pubAcl, privAcl, inTheClear);
1108 }
1109
1110
1111 //
1112 // Given a "blobbed" key for this database, decode it into its real
1113 // key object and (re)populate its ACL.
1114 //
1115 void KeychainDatabase::decodeKey(KeyBlob *blob, CssmKey &key, void * &pubAcl, void * &privAcl)
1116 {
1117 StLock<Mutex> _(common());
1118
1119 if(!blob->isClearText())
1120 makeUnlocked(); // we need our keys
1121
1122 common().decodeKeyCore(blob, key, pubAcl, privAcl);
1123 // memory protocol: pubAcl points into blob; privAcl was allocated
1124
1125 activity();
1126 }
1127
1128 //
1129 // Given a KeychainKey (that implicitly belongs to another keychain),
1130 // return it encoded using this keychain's operational secrets.
1131 //
1132 KeyBlob *KeychainDatabase::recodeKey(KeychainKey &oldKey)
1133 {
1134 if (mRecodingSource != &oldKey.referent<KeychainDatabase>()) {
1135 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
1136 }
1137 oldKey.instantiateAcl(); // make sure key is decoded
1138 CssmData publicAcl, privateAcl;
1139 oldKey.exportBlob(publicAcl, privateAcl);
1140 // NB: blob's memory belongs to caller, not the common
1141
1142 /*
1143 * Make sure the new key is in the same cleartext/encrypted state.
1144 */
1145 bool inTheClear = false;
1146 assert(oldKey.blob());
1147 if(oldKey.blob() && oldKey.blob()->isClearText()) {
1148 /* careful....*/
1149 inTheClear = true;
1150 }
1151 KeyBlob *blob = common().encodeKeyCore(oldKey.cssmKey(), publicAcl, privateAcl, inTheClear);
1152 oldKey.acl().allocator.free(publicAcl);
1153 oldKey.acl().allocator.free(privateAcl);
1154 return blob;
1155 }
1156
1157
1158 //
1159 // Modify database parameters
1160 //
1161 void KeychainDatabase::setParameters(const DBParameters &params)
1162 {
1163 StLock<Mutex> _(common());
1164 makeUnlocked();
1165 common().mParams = params;
1166 common().invalidateBlob(); // invalidate old blobs
1167 activity(); // (also resets the timeout timer)
1168 secdebug("KCdb", "%p common %p(%s) set params=(%u,%u)",
1169 this, &common(), dbName(), params.idleTimeout, params.lockOnSleep);
1170 }
1171
1172
1173 //
1174 // Retrieve database parameters
1175 //
1176 void KeychainDatabase::getParameters(DBParameters &params)
1177 {
1178 StLock<Mutex> _(common());
1179 makeUnlocked();
1180 params = common().mParams;
1181 //activity(); // getting parameters does not reset the idle timer
1182 }
1183
1184
1185 //
1186 // RIGHT NOW, database ACLs are attached to the database.
1187 // This will soon move upstairs.
1188 //
1189 SecurityServerAcl &KeychainDatabase::acl()
1190 {
1191 return *this;
1192 }
1193
1194
1195 //
1196 // Intercept ACL change requests and reset blob validity
1197 //
1198 void KeychainDatabase::instantiateAcl()
1199 {
1200 StLock<Mutex> _(common());
1201 makeUnlocked();
1202 }
1203
1204 void KeychainDatabase::changedAcl()
1205 {
1206 StLock<Mutex> _(common());
1207 version = 0;
1208 }
1209
1210
1211 //
1212 // Check an incoming DbBlob for basic viability
1213 //
1214 void KeychainDatabase::validateBlob(const DbBlob *blob)
1215 {
1216 // perform basic validation on the blob
1217 assert(blob);
1218 blob->validate(CSSMERR_APPLEDL_INVALID_DATABASE_BLOB);
1219 switch (blob->version()) {
1220 #if defined(COMPAT_OSX_10_0)
1221 case DbBlob::version_MacOS_10_0:
1222 break;
1223 #endif
1224 case DbBlob::version_MacOS_10_1:
1225 break;
1226 default:
1227 CssmError::throwMe(CSSMERR_APPLEDL_INCOMPATIBLE_DATABASE_BLOB);
1228 }
1229 }
1230
1231
1232 //
1233 // Debugging support
1234 //
1235 #if defined(DEBUGDUMP)
1236
1237 void KeychainDbCommon::dumpNode()
1238 {
1239 PerSession::dumpNode();
1240 uint32 sig; memcpy(&sig, &mIdentifier.signature(), sizeof(sig));
1241 Debug::dump(" %s[%8.8x]", mIdentifier.dbName(), sig);
1242 if (isLocked()) {
1243 Debug::dump(" locked");
1244 } else {
1245 time_t whenTime = time_t(when());
1246 Debug::dump(" unlocked(%24.24s/%.2g)", ctime(&whenTime),
1247 (when() - Time::now()).seconds());
1248 }
1249 Debug::dump(" params=(%u,%u)", mParams.idleTimeout, mParams.lockOnSleep);
1250 }
1251
1252 void KeychainDatabase::dumpNode()
1253 {
1254 PerProcess::dumpNode();
1255 Debug::dump(" %s vers=%u",
1256 mValidData ? " data" : " nodata", version);
1257 if (mBlob) {
1258 uint32 sig; memcpy(&sig, &mBlob->randomSignature, sizeof(sig));
1259 Debug::dump(" blob=%p[%8.8x]", mBlob, sig);
1260 } else {
1261 Debug::dump(" noblob");
1262 }
1263 }
1264
1265 #endif //DEBUGDUMP
1266
1267
1268 //
1269 // DbCommon basic features
1270 //
1271 KeychainDbCommon::KeychainDbCommon(Session &ssn, const DbIdentifier &id)
1272 : LocalDbCommon(ssn), sequence(0), version(1), mIdentifier(id),
1273 mIsLocked(true), mValidParams(false), mLoginKeychain(false)
1274 {
1275 // match existing DbGlobal or create a new one
1276 {
1277 Server &server = Server::active();
1278 StLock<Mutex> _(server);
1279 if (KeychainDbGlobal *dbglobal =
1280 server.findFirst<KeychainDbGlobal, const DbIdentifier &>(&KeychainDbGlobal::identifier, identifier())) {
1281 parent(*dbglobal);
1282 secdebug("KCdb", "%p linking to existing DbGlobal %p", this, dbglobal);
1283 } else {
1284 // DbGlobal not present; make a new one
1285 parent(*new KeychainDbGlobal(identifier()));
1286 secdebug("KCdb", "%p linking to new DbGlobal %p", this, &global());
1287 }
1288
1289 // link lifetime to the Session
1290 session().addReference(*this);
1291
1292 if (strcasestr(id.dbName(), "login.keychain") != NULL) {
1293 mLoginKeychain = true;
1294 }
1295 }
1296
1297 if (mLoginKeychain && !session().keybagGetState(session_keybag_loaded)) {
1298 service_context_t context = session().get_current_service_context();
1299 if (service_client_kb_load(&context) == 0) {
1300 session().keybagSetState(session_keybag_loaded);
1301 }
1302 }
1303 }
1304
1305 KeychainDbCommon::~KeychainDbCommon()
1306 {
1307 SECURITYD_KEYCHAIN_RELEASE(this, (char*)this->dbName());
1308
1309 // explicitly unschedule ourselves
1310 Server::active().clearTimer(this);
1311 if (mLoginKeychain) {
1312 session().keybagClearState(session_keybag_unlocked);
1313 }
1314 }
1315
1316 KeychainDbGlobal &KeychainDbCommon::global() const
1317 {
1318 return parent<KeychainDbGlobal>();
1319 }
1320
1321
1322 void KeychainDbCommon::select()
1323 { this->ref(); }
1324
1325 void KeychainDbCommon::unselect()
1326 { this->unref(); }
1327
1328
1329
1330 void KeychainDbCommon::makeNewSecrets()
1331 {
1332 // we already have a master key (right?)
1333 assert(hasMaster());
1334
1335 // tell crypto core to generate the use keys
1336 DatabaseCryptoCore::generateNewSecrets();
1337
1338 // we're now officially "unlocked"; set the timer
1339 setUnlocked();
1340 }
1341
1342
1343 //
1344 // All unlocking activity ultimately funnels through this method.
1345 // This unlocks a DbCommon using the secrets setup in its crypto core
1346 // component, and performs all the housekeeping needed to represent
1347 // the state change.
1348 // Returns true if unlock was successful, false if it failed due to
1349 // invalid/insufficient secrets. Throws on other errors.
1350 //
1351 bool KeychainDbCommon::unlockDb(DbBlob *blob, void **privateAclBlob)
1352 {
1353 try {
1354 // Tell the cryptocore to (try to) decode itself. This will fail
1355 // in an astonishing variety of ways if the passphrase is wrong.
1356 assert(hasMaster());
1357 decodeCore(blob, privateAclBlob);
1358 secdebug("KCdb", "%p unlock successful", this);
1359 } catch (...) {
1360 secdebug("KCdb", "%p unlock failed", this);
1361 return false;
1362 }
1363
1364 // get the database parameters only if we haven't got them yet
1365 if (!mValidParams) {
1366 mParams = blob->params;
1367 n2hi(mParams.idleTimeout);
1368 mValidParams = true; // sticky
1369 }
1370
1371 bool isLocked = mIsLocked;
1372
1373 setUnlocked(); // mark unlocked
1374
1375 if (isLocked) {
1376 // broadcast unlock notification, but only if we were previously locked
1377 notify(kNotificationEventUnlocked);
1378 SECURITYD_KEYCHAIN_UNLOCK(this, (char*)this->dbName());
1379 }
1380 return true;
1381 }
1382
1383 void KeychainDbCommon::setUnlocked()
1384 {
1385 session().addReference(*this); // active/held
1386 mIsLocked = false; // mark unlocked
1387 activity(); // set timeout timer
1388 }
1389
1390
1391 void KeychainDbCommon::lockDb()
1392 {
1393 bool lock = false;
1394 {
1395 StLock<Mutex> _(*this);
1396 if (!isLocked()) {
1397 DatabaseCryptoCore::invalidate();
1398 notify(kNotificationEventLocked);
1399 SECURITYD_KEYCHAIN_LOCK(this, (char*)this->dbName());
1400 Server::active().clearTimer(this);
1401
1402 mIsLocked = true; // mark locked
1403 lock = true;
1404
1405 // this call may destroy us if we have no databases anymore
1406 session().removeReference(*this);
1407 }
1408 }
1409
1410 if (mLoginKeychain && lock) {
1411 service_context_t context = session().get_current_service_context();
1412 service_client_kb_lock(&context);
1413 session().keybagClearState(session_keybag_unlocked);
1414 }
1415 }
1416
1417
1418 DbBlob *KeychainDbCommon::encode(KeychainDatabase &db)
1419 {
1420 assert(!isLocked()); // must have been unlocked by caller
1421
1422 // export database ACL to blob form
1423 CssmData pubAcl, privAcl;
1424 db.acl().exportBlob(pubAcl, privAcl);
1425
1426 // tell the cryptocore to form the blob
1427 DbBlob form;
1428 form.randomSignature = identifier();
1429 form.sequence = sequence;
1430 form.params = mParams;
1431 h2ni(form.params.idleTimeout);
1432
1433 assert(hasMaster());
1434 DbBlob *blob = encodeCore(form, pubAcl, privAcl);
1435
1436 // clean up and go
1437 db.acl().allocator.free(pubAcl);
1438 db.acl().allocator.free(privAcl);
1439 return blob;
1440 }
1441
1442
1443 //
1444 // Perform deferred lock processing for a database.
1445 //
1446 void KeychainDbCommon::action()
1447 {
1448 secdebug("KCdb", "common %s(%p) locked by timer", dbName(), this);
1449 lockDb();
1450 }
1451
1452 void KeychainDbCommon::activity()
1453 {
1454 if (!isLocked()) {
1455 secdebug("KCdb", "setting DbCommon %p timer to %d",
1456 this, int(mParams.idleTimeout));
1457 Server::active().setTimer(this, Time::Interval(int(mParams.idleTimeout)));
1458 }
1459 }
1460
1461 void KeychainDbCommon::sleepProcessing()
1462 {
1463 secdebug("KCdb", "common %s(%p) sleep-lock processing", dbName(), this);
1464 StLock<Mutex> _(*this);
1465 if (mParams.lockOnSleep)
1466 lockDb();
1467 }
1468
1469 void KeychainDbCommon::lockProcessing()
1470 {
1471 lockDb();
1472 }
1473
1474
1475 //
1476 // We consider a keychain to belong to the system domain if it resides
1477 // in /Library/Keychains. That's not exactly fool-proof, but we don't
1478 // currently have any internal markers to interrogate.
1479 //
1480 bool KeychainDbCommon::belongsToSystem() const
1481 {
1482 if (const char *name = this->dbName())
1483 return !strncmp(name, "/Library/Keychains/", 19);
1484 return false;
1485 }
1486
1487
1488 //
1489 // Keychain global objects
1490 //
1491 KeychainDbGlobal::KeychainDbGlobal(const DbIdentifier &id)
1492 : mIdentifier(id)
1493 {
1494 }
1495
1496 KeychainDbGlobal::~KeychainDbGlobal()
1497 {
1498 secdebug("KCdb", "DbGlobal %p destroyed", this);
1499 }