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