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