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