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