]> git.saurik.com Git - apple/securityd.git/blob - src/kcdatabase.cpp
e41c3d7cc1fd7d7435e521cd66a5b297058ef793
[apple/securityd.git] / src / kcdatabase.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, 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_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
57
58 //
59 // Create a Database object from initial parameters (create operation)
60 //
61 KeychainDatabase::KeychainDatabase(const DLDbIdentifier &id, const DBParameters &params, Process &proc,
62 const AccessCredentials *cred, const AclEntryPrototype *owner)
63 : LocalDatabase(proc), mValidData(false), version(0), mBlob(NULL)
64 {
65 // save a copy of the credentials for later access control
66 mCred = DataWalkers::copy(cred, Allocator::standard());
67
68 // create a new random signature to complete the DLDbIdentifier
69 DbBlob::Signature newSig;
70 Server::active().random(newSig.bytes);
71 DbIdentifier ident(id, newSig);
72
73 // create common block and initialize
74 RefPointer<KeychainDbCommon> newCommon = new KeychainDbCommon(proc.session(), ident);
75 StLock<Mutex> _(*newCommon);
76 parent(*newCommon);
77 // new common is now visible (in ident-map) but we hold its lock
78
79 // establish the new master secret
80 establishNewSecrets(cred, SecurityAgent::newDatabase);
81
82 // set initial database parameters
83 common().mParams = params;
84
85 // the common is "unlocked" now
86 common().makeNewSecrets();
87
88 // establish initial ACL
89 if (owner)
90 acl().cssmSetInitial(*owner);
91 else
92 acl().cssmSetInitial(new AnyAclSubject());
93 mValidData = true;
94
95 // for now, create the blob immediately
96 encode();
97
98 proc.addReference(*this);
99
100 // this new keychain is unlocked; make it so
101 activity();
102
103 secdebug("KCdb", "database %s(%p) created, common at %p",
104 common().dbName(), this, &common());
105 }
106
107
108 //
109 // Create a Database object from a database blob (decoding)
110 //
111 KeychainDatabase::KeychainDatabase(const DLDbIdentifier &id, const DbBlob *blob, Process &proc,
112 const AccessCredentials *cred)
113 : LocalDatabase(proc), mValidData(false), version(0)
114 {
115 validateBlob(blob);
116
117 // save a copy of the credentials for later access control
118 mCred = DataWalkers::copy(cred, Allocator::standard());
119 mBlob = blob->copy();
120
121 // check to see if we already know about this database
122 DbIdentifier ident(id, blob->randomSignature);
123 Session &session = process().session();
124 StLock<Mutex> _(session);
125 if (KeychainDbCommon *dbcom =
126 session.findFirst<KeychainDbCommon, const DbIdentifier &>(&KeychainDbCommon::identifier, ident)) {
127 parent(*dbcom);
128 //@@@ arbitrate sequence number here, perhaps update common().mParams
129 secdebug("KCdb",
130 "open database %s(%p) version %lx at known common %p",
131 common().dbName(), this, blob->version(), &common());
132 } else {
133 // DbCommon not present; make a new one
134 parent(*new KeychainDbCommon(proc.session(), ident));
135 common().mParams = blob->params;
136 secdebug("KCdb", "open database %s(%p) version %lx with new common %p",
137 common().dbName(), this, blob->version(), &common());
138 // this DbCommon is locked; no timer or reference setting
139 }
140 proc.addReference(*this);
141 }
142
143
144 //
145 // Special-purpose constructor for keychain synchronization. Copies an
146 // existing keychain but uses the operational keys from secretsBlob. The
147 // new KeychainDatabase will silently replace the existing KeychainDatabase
148 // as soon as the client declares that re-encoding of all keychain items is
149 // finished. This is a little perilous since it allows a client to dictate
150 // securityd state, but we try to ensure that only the client that started
151 // the re-encoding can declare it done.
152 //
153 KeychainDatabase::KeychainDatabase(KeychainDatabase &src, Process &proc,
154 const DbBlob *secretsBlob, const CssmData &agentData)
155 : LocalDatabase(proc), mValidData(false), version(0), mBlob(NULL)
156 {
157 validateBlob(secretsBlob);
158
159 // get the passphrase to unlock secretsBlob
160 QueryDBBlobSecret query;
161 query.inferHints(proc);
162 query.addHint(AGENT_HINT_KCSYNC_DICT, agentData.data(), agentData.length());
163 DatabaseCryptoCore keysCore;
164 if (query(keysCore, secretsBlob) != SecurityAgent::noReason)
165 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
166 // keysCore is now ready to yield its secrets to us
167
168 mCred = DataWalkers::copy(src.mCred, Allocator::standard());
169
170 // Give this KeychainDatabase a temporary name
171 std::string newDbName = std::string("////") + std::string(src.identifier().dbName());
172 DLDbIdentifier newDLDbIdent(src.identifier().dlDbIdentifier().ssuid(), newDbName.c_str(), src.identifier().dlDbIdentifier().dbLocation());
173 DbIdentifier ident(newDLDbIdent, src.identifier());
174
175 // create common block and initialize
176 RefPointer<KeychainDbCommon> newCommon = new KeychainDbCommon(proc.session(), ident);
177 StLock<Mutex> _(*newCommon);
178 parent(*newCommon);
179
180 // set initial database parameters from the source keychain
181 common().mParams = src.common().mParams;
182
183 // establish the source keychain's master secret as ours
184 // @@@ NB: this is a v. 0.1 assumption. We *should* trigger new UI
185 // that offers the user the option of using the existing password
186 // or choosing a new one. That would require a new
187 // SecurityAgentQuery type, new UI, and--possibly--modifications to
188 // ensure that the new password is available here to generate the
189 // new master secret.
190 src.unlockDb(); // precaution for masterKey()
191 common().setup(src.blob(), src.common().masterKey());
192
193 // import the operational secrets
194 common().importSecrets(keysCore);
195
196 // import source keychain's ACL
197 CssmData pubAcl, privAcl;
198 src.acl().exportBlob(pubAcl, privAcl);
199 importBlob(pubAcl.data(), privAcl.data());
200 src.acl().allocator.free(pubAcl);
201 src.acl().allocator.free(privAcl);
202
203 // indicate that this keychain should be allowed to do some otherwise
204 // risky things required for copying, like re-encoding keys
205 mRecodingSource = &src;
206
207 common().setUnlocked();
208 mValidData = true;
209
210 encode();
211
212 proc.addReference(*this);
213 secdebug("SSdb", "database %s(%p) created as copy, common at %p",
214 common().dbName(), this, &common());
215 }
216
217
218 //
219 // Destroy a Database
220 //
221 KeychainDatabase::~KeychainDatabase()
222 {
223 secdebug("KCdb", "deleting database %s(%p) common %p",
224 common().dbName(), this, &common());
225 Allocator::standard().free(mCred);
226 Allocator::standard().free(mBlob);
227 }
228
229
230 //
231 // Basic Database virtual implementations
232 //
233 KeychainDbCommon &KeychainDatabase::common() const
234 {
235 return parent<KeychainDbCommon>();
236 }
237
238 const char *KeychainDatabase::dbName() const
239 {
240 return common().dbName();
241 }
242
243 bool KeychainDatabase::transient() const
244 {
245 return false; // has permanent store
246 }
247
248 AclKind KeychainDatabase::aclKind() const
249 {
250 return dbAcl;
251 }
252
253 Database *KeychainDatabase::relatedDatabase()
254 {
255 return this;
256 }
257
258
259 static inline KeychainKey &myKey(Key *key)
260 {
261 return *safe_cast<KeychainKey *>(key);
262 }
263
264
265 //
266 // (Re-)Authenticate the database. This changes the stored credentials.
267 //
268 void KeychainDatabase::authenticate(CSSM_DB_ACCESS_TYPE mode,
269 const AccessCredentials *cred)
270 {
271 StLock<Mutex> _(common());
272
273 // the (Apple specific) RESET bit means "lock the database now"
274 switch (mode) {
275 case CSSM_DB_ACCESS_RESET:
276 secdebug("KCdb", "%p ACCESS_RESET triggers keychain lock", this);
277 common().lockDb();
278 break;
279 default:
280 // store the new credentials for future use
281 secdebug("KCdb", "%p authenticate stores new database credentials", this);
282 AccessCredentials *newCred = DataWalkers::copy(cred, Allocator::standard());
283 Allocator::standard().free(mCred);
284 mCred = newCred;
285 }
286 }
287
288
289 //
290 // Make a new KeychainKey.
291 // If PERMANENT is off, make a temporary key instead.
292 // The db argument allows you to create for another KeychainDatabase (only);
293 // it defaults to ourselves.
294 //
295 RefPointer<Key> KeychainDatabase::makeKey(Database &db, const CssmKey &newKey,
296 uint32 moreAttributes, const AclEntryPrototype *owner)
297 {
298
299 if (moreAttributes & CSSM_KEYATTR_PERMANENT)
300 return new KeychainKey(db, newKey, moreAttributes, owner);
301 else
302 return process().makeTemporaryKey(newKey, moreAttributes, owner);
303 }
304
305 RefPointer<Key> KeychainDatabase::makeKey(const CssmKey &newKey,
306 uint32 moreAttributes, const AclEntryPrototype *owner)
307 {
308 return makeKey(*this, newKey, moreAttributes, owner);
309 }
310
311
312 //
313 // Return the database blob, recalculating it as needed.
314 //
315 DbBlob *KeychainDatabase::blob()
316 {
317 StLock<Mutex> _(common());
318 if (!validBlob()) {
319 makeUnlocked(); // unlock to get master secret
320 encode(); // (re)encode blob if needed
321 }
322 activity(); // reset timeout
323 assert(validBlob()); // better have a valid blob now...
324 return mBlob;
325 }
326
327
328 //
329 // Encode the current database as a blob.
330 // Note that this returns memory we own and keep.
331 // Caller must hold common lock.
332 //
333 void KeychainDatabase::encode()
334 {
335 DbBlob *blob = common().encode(*this);
336 Allocator::standard().free(mBlob);
337 mBlob = blob;
338 version = common().version;
339 secdebug("KCdb", "encoded database %p common %p(%s) version %ld params=(%ld,%d)",
340 this, &common(), dbName(), version,
341 common().mParams.idleTimeout, common().mParams.lockOnSleep);
342 }
343
344
345 //
346 // Change the passphrase on a database
347 //
348 void KeychainDatabase::changePassphrase(const AccessCredentials *cred)
349 {
350 // get and hold the common lock (don't let other threads break in here)
351 StLock<Mutex> _(common());
352
353 // establish OLD secret - i.e. unlock the database
354 //@@@ do we want to leave the final lock state alone?
355 makeUnlocked(cred);
356
357 // establish NEW secret
358 establishNewSecrets(cred, SecurityAgent::changePassphrase);
359 common().invalidateBlob(); // blob state changed
360 secdebug("KCdb", "Database %s(%p) master secret changed", common().dbName(), this);
361 encode(); // force rebuild of local blob
362
363 // send out a notification
364 notify(kNotificationEventPassphraseChanged);
365
366 // I guess this counts as an activity
367 activity();
368 }
369
370 //
371 // Second stage of keychain synchronization: overwrite the original keychain's
372 // (this KeychainDatabase's) operational secrets
373 //
374 void KeychainDatabase::commitSecretsForSync(KeychainDatabase &cloneDb)
375 {
376 StLock<Mutex> _(common());
377
378 // try to detect spoofing
379 if (cloneDb.mRecodingSource != this)
380 CssmError::throwMe(CSSM_ERRCODE_INVALID_DB_HANDLE);
381
382 // in case we autolocked since starting the sync
383 unlockDb();
384 cloneDb.unlockDb();
385
386 // Decode all keys whose handles refer to this on-disk keychain so that
387 // if the holding client commits the key back to disk, it's encoded with
388 // the new operational secrets. The recoding client *must* hold a write
389 // lock for the on-disk keychain from the moment it starts recoding key
390 // items until after this call.
391 //
392 // @@@ This specific implementation is a workaround for 4003540.
393 std::vector<CSSM_HANDLE> handleList;
394 HandleObject::findAllRefs<KeychainKey>(handleList);
395 size_t count = handleList.size();
396 if (count > 0) {
397 for (unsigned int n = 0; n < count; ++n) {
398 RefPointer<KeychainKey> kckey =
399 HandleObject::findRefAndLock<KeychainKey>(handleList[n], CSSMERR_CSP_INVALID_KEY_REFERENCE);
400 StLock<Mutex> _(*kckey/*, true*/);
401 if (kckey->database().global().identifier() == identifier()) {
402 kckey->key(); // force decode
403 kckey->invalidateBlob();
404 secdebug("kcrecode", "changed extant key %p (proc %d)",
405 &*kckey, kckey->process().pid());
406 }
407 }
408 }
409
410 // it is now safe to replace the old op secrets
411 common().importSecrets(cloneDb.common());
412 common().invalidateBlob();
413 }
414
415
416 //
417 // Extract the database master key as a proper Key object.
418 //
419 RefPointer<Key> KeychainDatabase::extractMasterKey(Database &db,
420 const AccessCredentials *cred, const AclEntryPrototype *owner,
421 uint32 usage, uint32 attrs)
422 {
423 // get and hold common lock
424 StLock<Mutex> _(common());
425
426 // force lock to require re-validation of credentials
427 lockDb();
428
429 // unlock to establish master secret
430 makeUnlocked();
431
432 // extract the raw cryptographic key
433 CssmClient::WrapKey wrap(Server::csp(), CSSM_ALGID_NONE);
434 CssmKey key;
435 wrap(common().masterKey(), key);
436
437 // make the key object and return it
438 return makeKey(db, key, attrs & LocalKey::managedAttributes, owner);
439 }
440
441
442 //
443 // Unlock this database (if needed) by obtaining the master secret in some
444 // suitable way and then proceeding to unlock with it.
445 // Does absolutely nothing if the database is already unlocked.
446 // The makeUnlocked forms are identical except the assume the caller already
447 // holds the common lock.
448 //
449 void KeychainDatabase::unlockDb()
450 {
451 StLock<Mutex> _(common());
452 makeUnlocked();
453 }
454
455 void KeychainDatabase::makeUnlocked()
456 {
457 return makeUnlocked(mCred);
458 }
459
460 void KeychainDatabase::makeUnlocked(const AccessCredentials *cred)
461 {
462 if (isLocked()) {
463 secdebug("KCdb", "%p(%p) unlocking for makeUnlocked()", this, &common());
464 assert(mBlob || (mValidData && common().hasMaster()));
465 establishOldSecrets(cred);
466 common().setUnlocked(); // mark unlocked
467 } else if (!mValidData) { // need to decode to get our ACLs, master secret available
468 secdebug("KCdb", "%p(%p) is unlocked; decoding for makeUnlocked()", this, &common());
469 if (!decode())
470 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
471 }
472 assert(!isLocked());
473 assert(mValidData);
474 }
475
476
477 //
478 // The following unlock given an explicit passphrase, rather than using
479 // (special cred sample based) default procedures.
480 //
481 void KeychainDatabase::unlockDb(const CssmData &passphrase)
482 {
483 StLock<Mutex> _(common());
484 makeUnlocked(passphrase);
485 }
486
487 void KeychainDatabase::makeUnlocked(const CssmData &passphrase)
488 {
489 if (isLocked()) {
490 if (decode(passphrase))
491 return;
492 else
493 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
494 } else if (!mValidData) { // need to decode to get our ACLs, passphrase available
495 if (!decode())
496 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
497 }
498 assert(!isLocked());
499 assert(mValidData);
500 }
501
502
503 //
504 // Nonthrowing passphrase-based unlock. This returns false if unlock failed.
505 // Note that this requires an explicitly given passphrase.
506 // Caller must hold common lock.
507 //
508 bool KeychainDatabase::decode(const CssmData &passphrase)
509 {
510 assert(mBlob);
511 common().setup(mBlob, passphrase);
512 return decode();
513 }
514
515
516 //
517 // Given the established master secret, decode the working keys and other
518 // functional secrets for this database. Return false (do NOT throw) if
519 // the decode fails. Call this in low(er) level code once you established
520 // the master key.
521 //
522 bool KeychainDatabase::decode()
523 {
524 assert(mBlob);
525 assert(common().hasMaster());
526 void *privateAclBlob;
527 if (common().unlockDb(mBlob, &privateAclBlob)) {
528 if (!mValidData) {
529 acl().importBlob(mBlob->publicAclBlob(), privateAclBlob);
530 mValidData = true;
531 }
532 Allocator::standard().free(privateAclBlob);
533 return true;
534 }
535 secdebug("KCdb", "%p decode failed", this);
536 return false;
537 }
538
539
540 //
541 // Given an AccessCredentials for this database, wring out the existing primary
542 // database secret by whatever means necessary.
543 // On entry, caller must hold the database common lock. It will be held throughout.
544 // On exit, the crypto core has its master secret. If things go wrong,
545 // we will throw a suitable exception. Note that encountering any malformed
546 // credential sample will throw, but this is not guaranteed -- don't assume
547 // that NOT throwing means creds is entirely well-formed (it may just be good
548 // enough to work THIS time).
549 //
550 // How this works:
551 // Walk through the creds. Fish out those credentials (in order) that
552 // are for unlock processing (they have no ACL subject correspondents),
553 // and (try to) obey each in turn, until one produces a valid secret
554 // or you run out. If no special samples are found at all, interpret that as
555 // "use the system global default," which happens to be hard-coded right here.
556 //
557 void KeychainDatabase::establishOldSecrets(const AccessCredentials *creds)
558 {
559 list<CssmSample> samples;
560 if (creds && creds->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, samples)) {
561 for (list<CssmSample>::iterator it = samples.begin(); it != samples.end(); it++) {
562 TypedList &sample = *it;
563 sample.checkProper();
564 switch (sample.type()) {
565 // interactively prompt the user - no additional data
566 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT:
567 {
568 secdebug("KCdb", "%p attempting interactive unlock", this);
569 QueryUnlock query(*this);
570 query.inferHints(Server::process());
571 if (query() == SecurityAgent::noReason)
572 return;
573 }
574 break;
575 // try to use an explicitly given passphrase - Data:passphrase
576 case CSSM_SAMPLE_TYPE_PASSWORD:
577 if (sample.length() != 2)
578 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
579 secdebug("KCdb", "%p attempting passphrase unlock", this);
580 if (decode(sample[1]))
581 return;
582 break;
583 // try to open with a given master key - Data:CSP or KeyHandle, Data:CssmKey
584 case CSSM_WORDID_SYMMETRIC_KEY:
585 assert(mBlob);
586 secdebug("KCdb", "%p attempting explicit key unlock", this);
587 common().setup(mBlob, keyFromCreds(sample, 4));
588 if (decode())
589 return;
590 break;
591 // explicitly defeat the default action but don't try anything in particular
592 case CSSM_WORDID_CANCELED:
593 secdebug("KCdb", "%p defeat default action", this);
594 break;
595 default:
596 // Unknown sub-sample for unlocking.
597 // If we wanted to be fascist, we could now do
598 // CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
599 // But instead we try to be tolerant and continue on.
600 // This DOES however count as an explicit attempt at specifying unlock,
601 // so we will no longer try the default case below...
602 secdebug("KCdb", "%p unknown sub-sample unlock (%ld) ignored", this, sample.type());
603 break;
604 }
605 }
606 } else {
607 // default action
608 assert(mBlob);
609
610 // attempt system-keychain unlock
611 SystemKeychainKey systemKeychain(kSystemUnlockFile);
612 if (systemKeychain.matches(mBlob->randomSignature)) {
613 secdebug("KCdb", "%p attempting system unlock", this);
614 common().setup(mBlob, CssmClient::Key(Server::csp(), systemKeychain.key(), true));
615 if (decode())
616 return;
617 }
618
619 // attempt interactive unlock
620 QueryUnlock query(*this);
621 query.inferHints(Server::process());
622 if (query() == SecurityAgent::noReason)
623 return;
624 }
625
626 // out of options - no secret obtained
627 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
628 }
629
630
631 //
632 // Same thing, but obtain a new secret somehow and set it into the common.
633 //
634 void KeychainDatabase::establishNewSecrets(const AccessCredentials *creds, SecurityAgent::Reason reason)
635 {
636 list<CssmSample> samples;
637 if (creds && creds->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK, samples)) {
638 for (list<CssmSample>::iterator it = samples.begin(); it != samples.end(); it++) {
639 TypedList &sample = *it;
640 sample.checkProper();
641 switch (sample.type()) {
642 // interactively prompt the user
643 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT:
644 {
645 secdebug("KCdb", "%p specified interactive passphrase", this);
646 QueryNewPassphrase query(*this, reason);
647 query.inferHints(Server::process());
648 CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
649 if (query(passphrase) == SecurityAgent::noReason) {
650 common().setup(NULL, passphrase);
651 return;
652 }
653 }
654 break;
655 // try to use an explicitly given passphrase
656 case CSSM_SAMPLE_TYPE_PASSWORD:
657 secdebug("KCdb", "%p specified explicit passphrase", this);
658 if (sample.length() != 2)
659 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
660 common().setup(NULL, sample[1]);
661 return;
662 // try to open with a given master key
663 case CSSM_WORDID_SYMMETRIC_KEY:
664 secdebug("KCdb", "%p specified explicit master key", this);
665 common().setup(NULL, keyFromCreds(sample, 3));
666 return;
667 // explicitly defeat the default action but don't try anything in particular
668 case CSSM_WORDID_CANCELED:
669 secdebug("KCdb", "%p defeat default action", this);
670 break;
671 default:
672 // Unknown sub-sample for acquiring new secret.
673 // If we wanted to be fascist, we could now do
674 // CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
675 // But instead we try to be tolerant and continue on.
676 // This DOES however count as an explicit attempt at specifying unlock,
677 // so we will no longer try the default case below...
678 secdebug("KCdb", "%p unknown sub-sample acquisition (%ld) ignored",
679 this, sample.type());
680 break;
681 }
682 }
683 } else {
684 // default action -- interactive (only)
685 QueryNewPassphrase query(*this, reason);
686 query.inferHints(Server::process());
687 CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
688 if (query(passphrase) == SecurityAgent::noReason) {
689 common().setup(NULL, passphrase);
690 return;
691 }
692 }
693
694 // out of options - no secret obtained
695 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
696 }
697
698
699 //
700 // Given a (truncated) Database credentials TypedList specifying a master key,
701 // locate the key and return a reference to it.
702 //
703 CssmClient::Key KeychainDatabase::keyFromCreds(const TypedList &sample, unsigned int requiredLength)
704 {
705 // decode TypedList structure (sample type; Data:CSPHandle; Data:CSSM_KEY)
706 assert(sample.type() == CSSM_WORDID_SYMMETRIC_KEY);
707 if (sample.length() != requiredLength
708 || sample[1].type() != CSSM_LIST_ELEMENT_DATUM
709 || sample[2].type() != CSSM_LIST_ELEMENT_DATUM
710 || (requiredLength == 4 && sample[3].type() != CSSM_LIST_ELEMENT_DATUM))
711 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
712 CSSM_CSP_HANDLE &handle = *sample[1].data().interpretedAs<CSSM_CSP_HANDLE>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
713 CssmKey &key = *sample[2].data().interpretedAs<CssmKey>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
714
715 if (key.header().cspGuid() == gGuidAppleCSPDL) {
716 // handleOrKey is a SecurityServer KeyHandle; ignore key argument
717 return safer_cast<LocalKey &>(*Server::key(handle));
718 } else {
719 // not a KeyHandle reference; use key as a raw key
720 if (key.header().blobType() != CSSM_KEYBLOB_RAW)
721 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE);
722 if (key.header().keyClass() != CSSM_KEYCLASS_SESSION_KEY)
723 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
724 return CssmClient::Key(Server::csp(), key, true);
725 }
726 }
727
728
729 //
730 // Verify a putative database passphrase.
731 // If the database is already unlocked, just check the passphrase.
732 // Otherwise, unlock with that passphrase and report success.
733 // Caller must hold the common lock.
734 //
735 bool KeychainDatabase::validatePassphrase(const CssmData &passphrase) const
736 {
737 if (common().hasMaster()) {
738 // verify against known secret
739 return common().validatePassphrase(passphrase);
740 } else {
741 // no master secret - perform "blind" unlock to avoid actual unlock
742 try {
743 DatabaseCryptoCore test;
744 test.setup(mBlob, passphrase);
745 test.decodeCore(mBlob, NULL);
746 return true;
747 } catch (...) {
748 return false;
749 }
750 }
751 }
752
753
754 //
755 // Lock this database
756 //
757 void KeychainDatabase::lockDb()
758 {
759 common().lockDb();
760 }
761
762
763 //
764 // Given a Key for this database, encode it into a blob and return it.
765 //
766 KeyBlob *KeychainDatabase::encodeKey(const CssmKey &key, const CssmData &pubAcl, const CssmData &privAcl)
767 {
768 unlockDb();
769
770 // tell the cryptocore to form the key blob
771 return common().encodeKeyCore(key, pubAcl, privAcl);
772 }
773
774
775 //
776 // Given a "blobbed" key for this database, decode it into its real
777 // key object and (re)populate its ACL.
778 //
779 void KeychainDatabase::decodeKey(KeyBlob *blob, CssmKey &key, void * &pubAcl, void * &privAcl)
780 {
781 unlockDb(); // we need our keys
782
783 common().decodeKeyCore(blob, key, pubAcl, privAcl);
784 // memory protocol: pubAcl points into blob; privAcl was allocated
785
786 activity();
787 }
788
789 //
790 // Given a KeychainKey (that implicitly belongs to another keychain),
791 // return it encoded using this keychain's operational secrets.
792 //
793 KeyBlob *KeychainDatabase::recodeKey(KeychainKey &oldKey)
794 {
795 if (mRecodingSource != &oldKey.referent<KeychainDatabase>()) {
796 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
797 }
798 oldKey.instantiateAcl(); // make sure key is decoded
799 CssmData publicAcl, privateAcl;
800 oldKey.exportBlob(publicAcl, privateAcl);
801 // NB: blob's memory belongs to caller, not the common
802 KeyBlob *blob = common().encodeKeyCore(oldKey.cssmKey(), publicAcl, privateAcl);
803 oldKey.acl().allocator.free(publicAcl);
804 oldKey.acl().allocator.free(privateAcl);
805 return blob;
806 }
807
808
809 //
810 // Modify database parameters
811 //
812 void KeychainDatabase::setParameters(const DBParameters &params)
813 {
814 StLock<Mutex> _(common());
815 makeUnlocked();
816 common().mParams = params;
817 common().invalidateBlob(); // invalidate old blobs
818 activity(); // (also resets the timeout timer)
819 secdebug("KCdb", "%p common %p(%s) set params=(%ld,%d)",
820 this, &common(), dbName(), params.idleTimeout, params.lockOnSleep);
821 }
822
823
824 //
825 // Retrieve database parameters
826 //
827 void KeychainDatabase::getParameters(DBParameters &params)
828 {
829 StLock<Mutex> _(common());
830 makeUnlocked();
831 params = common().mParams;
832 //activity(); // getting parameters does not reset the idle timer
833 }
834
835
836 //
837 // RIGHT NOW, database ACLs are attached to the database.
838 // This will soon move upstairs.
839 //
840 SecurityServerAcl &KeychainDatabase::acl()
841 {
842 return *this;
843 }
844
845
846 //
847 // Intercept ACL change requests and reset blob validity
848 //
849 void KeychainDatabase::instantiateAcl()
850 {
851 StLock<Mutex> _(common());
852 makeUnlocked();
853 }
854
855 void KeychainDatabase::changedAcl()
856 {
857 StLock<Mutex> _(common());
858 version = 0;
859 }
860
861
862 //
863 // Check an incoming DbBlob for basic viability
864 //
865 void KeychainDatabase::validateBlob(const DbBlob *blob)
866 {
867 // perform basic validation on the blob
868 assert(blob);
869 blob->validate(CSSMERR_APPLEDL_INVALID_DATABASE_BLOB);
870 switch (blob->version()) {
871 #if defined(COMPAT_OSX_10_0)
872 case blob->version_MacOS_10_0:
873 break;
874 #endif
875 case blob->version_MacOS_10_1:
876 break;
877 default:
878 CssmError::throwMe(CSSMERR_APPLEDL_INCOMPATIBLE_DATABASE_BLOB);
879 }
880 }
881
882
883 //
884 // Debugging support
885 //
886 #if defined(DEBUGDUMP)
887
888 void KeychainDbCommon::dumpNode()
889 {
890 PerSession::dumpNode();
891 uint32 sig; memcpy(&sig, &mIdentifier.signature(), sizeof(sig));
892 Debug::dump(" %s[%8.8lx]", mIdentifier.dbName(), sig);
893 if (isLocked()) {
894 Debug::dump(" locked");
895 } else {
896 time_t whenTime = time_t(when());
897 Debug::dump(" unlocked(%24.24s/%.2g)", ctime(&whenTime),
898 (when() - Time::now()).seconds());
899 }
900 Debug::dump(" params=(%ld,%d)", mParams.idleTimeout, mParams.lockOnSleep);
901 }
902
903 void KeychainDatabase::dumpNode()
904 {
905 PerProcess::dumpNode();
906 Debug::dump(" %s vers=%ld",
907 mValidData ? " data" : " nodata", version);
908 if (mBlob) {
909 uint32 sig; memcpy(&sig, &mBlob->randomSignature, sizeof(sig));
910 Debug::dump(" blob=%p[%8.8lx]", mBlob, sig);
911 } else {
912 Debug::dump(" noblob");
913 }
914 }
915
916 #endif //DEBUGDUMP
917
918
919 //
920 // DbCommon basic features
921 //
922 KeychainDbCommon::KeychainDbCommon(Session &ssn, const DbIdentifier &id)
923 : DbCommon(ssn), sequence(0), version(1), mIdentifier(id),
924 mIsLocked(true), mValidParams(false)
925 {
926 // match existing DbGlobal or create a new one
927 Server &server = Server::active();
928 StLock<Mutex> _(server);
929 if (KeychainDbGlobal *dbglobal =
930 server.findFirst<KeychainDbGlobal, const DbIdentifier &>(&KeychainDbGlobal::identifier, identifier())) {
931 parent(*dbglobal);
932 secdebug("KCdb", "%p linking to existing DbGlobal %p", this, dbglobal);
933 } else {
934 // DbGlobal not present; make a new one
935 parent(*new KeychainDbGlobal(identifier()));
936 secdebug("KCdb", "%p linking to new DbGlobal %p", this, &global());
937 }
938
939 // link lifetime to the Session
940 session().addReference(*this);
941 }
942
943 KeychainDbCommon::~KeychainDbCommon()
944 {
945 secdebug("KCdb", "DbCommon %p destroyed", this);
946
947 // explicitly unschedule ourselves
948 Server::active().clearTimer(this);
949 }
950
951 KeychainDbGlobal &KeychainDbCommon::global() const
952 {
953 return parent<KeychainDbGlobal>();
954 }
955
956
957 void KeychainDbCommon::makeNewSecrets()
958 {
959 // we already have a master key (right?)
960 assert(hasMaster());
961
962 // tell crypto core to generate the use keys
963 DatabaseCryptoCore::generateNewSecrets();
964
965 // we're now officially "unlocked"; set the timer
966 setUnlocked();
967 }
968
969
970 //
971 // All unlocking activity ultimately funnels through this method.
972 // This unlocks a DbCommon using the secrets setup in its crypto core
973 // component, and performs all the housekeeping needed to represent
974 // the state change.
975 // Returns true if unlock was successful, false if it failed due to
976 // invalid/insufficient secrets. Throws on other errors.
977 //
978 bool KeychainDbCommon::unlockDb(DbBlob *blob, void **privateAclBlob)
979 {
980 try {
981 // Tell the cryptocore to (try to) decode itself. This will fail
982 // in an astonishing variety of ways if the passphrase is wrong.
983 assert(hasMaster());
984 decodeCore(blob, privateAclBlob);
985 secdebug("KCdb", "%p unlock successful", this);
986 } catch (...) {
987 secdebug("KCdb", "%p unlock failed", this);
988 return false;
989 }
990
991 // get the database parameters only if we haven't got them yet
992 if (!mValidParams) {
993 mParams = blob->params;
994 n2hi(mParams.idleTimeout);
995 mValidParams = true; // sticky
996 }
997
998 setUnlocked(); // mark unlocked
999
1000 // broadcast unlock notification
1001 notify(kNotificationEventUnlocked);
1002 return true;
1003 }
1004
1005 void KeychainDbCommon::setUnlocked()
1006 {
1007 session().addReference(*this); // active/held
1008 mIsLocked = false; // mark unlocked
1009 activity(); // set timeout timer
1010 }
1011
1012
1013 void KeychainDbCommon::lockDb()
1014 {
1015 StLock<Mutex> _(*this);
1016 if (!isLocked()) {
1017 secdebug("KCdb", "common %s(%p) locking", dbName(), this);
1018 DatabaseCryptoCore::invalidate();
1019 notify(kNotificationEventLocked);
1020 Server::active().clearTimer(this);
1021
1022 mIsLocked = true; // mark locked
1023
1024 // this call may destroy us if we have no databases anymore
1025 session().removeReference(*this);
1026 }
1027 }
1028
1029
1030 DbBlob *KeychainDbCommon::encode(KeychainDatabase &db)
1031 {
1032 assert(!isLocked()); // must have been unlocked by caller
1033
1034 // export database ACL to blob form
1035 CssmData pubAcl, privAcl;
1036 db.acl().exportBlob(pubAcl, privAcl);
1037
1038 // tell the cryptocore to form the blob
1039 DbBlob form;
1040 form.randomSignature = identifier();
1041 form.sequence = sequence;
1042 form.params = mParams;
1043 h2ni(form.params.idleTimeout);
1044
1045 assert(hasMaster());
1046 DbBlob *blob = encodeCore(form, pubAcl, privAcl);
1047
1048 // clean up and go
1049 db.acl().allocator.free(pubAcl);
1050 db.acl().allocator.free(privAcl);
1051 return blob;
1052 }
1053
1054
1055 //
1056 // Send a keychain-related notification event about this keychain
1057 //
1058 void KeychainDbCommon::notify(NotificationEvent event)
1059 {
1060 // form the data (encoded DLDbIdentifier)
1061 NameValueDictionary nvd;
1062 NameValueDictionary::MakeNameValueDictionaryFromDLDbIdentifier(identifier(), nvd);
1063 CssmData data;
1064 nvd.Export(data);
1065
1066 // inject notification into Security event system
1067 Listener::notify(kNotificationDomainDatabase, event, data);
1068
1069 // clean up
1070 free (data.data());
1071 }
1072
1073
1074 //
1075 // Perform deferred lock processing for a database.
1076 //
1077 void KeychainDbCommon::action()
1078 {
1079 secdebug("KCdb", "common %s(%p) locked by timer", dbName(), this);
1080 lockDb();
1081 }
1082
1083 void KeychainDbCommon::activity()
1084 {
1085 if (!isLocked()) {
1086 secdebug("KCdb", "setting DbCommon %p timer to %d",
1087 this, int(mParams.idleTimeout));
1088 Server::active().setTimer(this, Time::Interval(int(mParams.idleTimeout)));
1089 }
1090 }
1091
1092 void KeychainDbCommon::sleepProcessing()
1093 {
1094 secdebug("KCdb", "common %s(%p) sleep-lock processing", dbName(), this);
1095 StLock<Mutex> _(*this);
1096 if (mParams.lockOnSleep)
1097 lockDb();
1098 }
1099
1100 void KeychainDbCommon::lockProcessing()
1101 {
1102 lockDb();
1103 }
1104
1105
1106 //
1107 // Keychain global objects
1108 //
1109 KeychainDbGlobal::KeychainDbGlobal(const DbIdentifier &id)
1110 : mIdentifier(id)
1111 {
1112 }
1113
1114 KeychainDbGlobal::~KeychainDbGlobal()
1115 {
1116 secdebug("KCdb", "DbGlobal %p destroyed", this);
1117 }