]> git.saurik.com Git - apple/security.git/blob - securityd/src/kcdatabase.cpp
Security-57740.1.18.tar.gz
[apple/security.git] / securityd / src / kcdatabase.cpp
1 /*
2 * Copyright (c) 2000-2009,2012-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 //
26 // kcdatabase - software database container implementation.
27 //
28 // General implementation notes:
29 // This leverages LocalDatabase/LocalKey for cryptography, and adds the
30 // storage coder/decoder logic that implements "keychain" databases in their
31 // intricately choreographed dance between securityd and the AppleCSPDL.
32 // As always, Database objects are lifetime-bound to their Process referent;
33 // they can also be destroyed explicitly with a client release call.
34 // DbCommons are reference-held by their Databases, with one extra special
35 // reference (from the Session) introduced when the database unlocks, and
36 // removed when it locks again. That way, an unused DbCommon dies when it
37 // is locked or when the Session dies, whichever happens earlier.
38 // There is (as yet) no global-scope Database object for Keychain databases.
39 //
40 #include "kcdatabase.h"
41 #include "agentquery.h"
42 #include "kckey.h"
43 #include "server.h"
44 #include "session.h"
45 #include "notifications.h"
46 #include <vector> // @@@ 4003540 workaround
47 #include <security_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 #include <sys/csr.h>
62
63 void unflattenKey(const CssmData &flatKey, CssmKey &rawKey); //>> make static method on KeychainDatabase
64
65 //
66 // Static members
67 //
68 KeychainDbCommon::CommonSet KeychainDbCommon::mCommonSet;
69 ReadWriteLock KeychainDbCommon::mRWCommonLock;
70
71 // Process is using a cached effective uid, login window switches uid after the intial connection
72 static void get_process_euid(pid_t pid, uid_t * out_euid)
73 {
74 if (!out_euid) return;
75
76 struct kinfo_proc proc_info = {};
77 int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
78 size_t len = sizeof(struct kinfo_proc);
79 int ret;
80 ret = sysctl(mib, (sizeof(mib)/sizeof(int)), &proc_info, &len, NULL, 0);
81
82 // don't allow root
83 if ((ret == 0) && (proc_info.kp_eproc.e_ucred.cr_uid != 0)) {
84 *out_euid = proc_info.kp_eproc.e_ucred.cr_uid;
85 }
86 }
87
88 static int
89 unlock_keybag(KeychainDatabase & db, const void * secret, int secret_len)
90 {
91 int rc = -1;
92
93 if (!db.common().isLoginKeychain()) return 0;
94
95 service_context_t context = db.common().session().get_current_service_context();
96
97 // login window attempts to change the password before a session has a uid
98 if (context.s_uid == AU_DEFAUDITID) {
99 get_process_euid(db.process().pid(), &context.s_uid);
100 }
101
102 // try to unlock first if not found then load/create or unlock
103 // loading should happen when the kb common object is created
104 // if it doesn't exist yet then the unlock will fail and we'll create everything
105 rc = service_client_kb_unlock(&context, secret, secret_len);
106 if (rc == KB_BagNotLoaded) {
107 if (service_client_kb_load(&context) == KB_BagNotFound) {
108 rc = service_client_kb_create(&context, secret, secret_len);
109 } else {
110 rc = service_client_kb_unlock(&context, secret, secret_len);
111 }
112 }
113
114 if (rc != 0) { // if we just upgraded make sure we swap the encryption key to the password
115 if (!db.common().session().keybagGetState(session_keybag_check_master_key)) {
116 CssmAutoData encKey(Allocator::standard(Allocator::sensitive));
117 db.common().get_encryption_key(encKey);
118 if ((rc = service_client_kb_unlock(&context, encKey.data(), (int)encKey.length())) == 0) {
119 rc = service_client_kb_change_secret(&context, encKey.data(), (int)encKey.length(), secret, secret_len);
120 }
121
122 if (rc != 0) { // if a login.keychain password exists but doesnt on the keybag update it
123 bool no_pin = false;
124 if ((secret_len > 0) && service_client_kb_is_locked(&context, NULL, &no_pin) == 0) {
125 if (no_pin) {
126 syslog(LOG_ERR, "Updating iCloud keychain passphrase for uid %d", context.s_uid);
127 service_client_kb_change_secret(&context, NULL, 0, secret, secret_len);
128 }
129 }
130 }
131 } // session_keybag_check_master_key
132 }
133
134 if (rc == 0) {
135 db.common().session().keybagSetState(session_keybag_unlocked|session_keybag_loaded|session_keybag_check_master_key);
136 } else {
137 syslog(LOG_ERR, "Failed to unlock iCloud keychain for uid %d", context.s_uid);
138 }
139
140 return rc;
141 }
142
143 static void
144 change_secret_on_keybag(KeychainDatabase & db, const void * secret, int secret_len, const void * new_secret, int new_secret_len)
145 {
146 if (!db.common().isLoginKeychain()) return;
147
148 service_context_t context = db.common().session().get_current_service_context();
149
150 // login window attempts to change the password before a session has a uid
151 if (context.s_uid == AU_DEFAUDITID) {
152 get_process_euid(db.process().pid(), &context.s_uid);
153 }
154
155 // if a login.keychain doesn't exist yet it comes into securityd as a create then change_secret
156 // we need to create the keybag in this case if it doesn't exist
157 int rc = service_client_kb_change_secret(&context, secret, secret_len, new_secret, new_secret_len);
158 if (rc == KB_BagNotLoaded) {
159 if (service_client_kb_load(&context) == KB_BagNotFound) {
160 rc = service_client_kb_create(&context, new_secret, new_secret_len);
161 } else {
162 rc = service_client_kb_change_secret(&context, secret, secret_len, new_secret, new_secret_len);
163 }
164 }
165
166 // this makes it possible to restore a deleted keybag on condition it still exists in kernel
167 if (rc != KB_Success) {
168 service_client_kb_save(&context);
169 }
170
171 // if for some reason we are locked lets unlock so later we don't try and throw up SecurityAgent dialog
172 bool locked = false;
173 if ((service_client_kb_is_locked(&context, &locked, NULL) == KB_Success) && locked) {
174 service_client_kb_unlock(&context, new_secret, new_secret_len);
175 }
176 }
177
178 // Attempt to unlock the keybag with a AccessCredentials password.
179 // Honors UI disabled flags from clients set in the cred before prompt.
180 static bool
181 unlock_keybag_with_cred(KeychainDatabase &db, const AccessCredentials *cred){
182 list<CssmSample> samples;
183 if (cred && cred->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, samples)) {
184 for (list<CssmSample>::iterator it = samples.begin(); it != samples.end(); it++) {
185 TypedList &sample = *it;
186 sample.checkProper();
187 switch (sample.type()) {
188 // interactively prompt the user - no additional data
189 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT: {
190 StSyncLock<Mutex, Mutex> uisync(db.common().uiLock(), db.common());
191 // Once we get the ui lock, check whether another thread has already unlocked keybag
192 bool locked = false;
193 service_context_t context = db.common().session().get_current_service_context();
194 if ((service_client_kb_is_locked(&context, &locked, NULL) == 0) && locked) {
195 QueryKeybagPassphrase keybagQuery(db.common().session(), 3);
196 keybagQuery.inferHints(Server::process());
197 if (keybagQuery.query() == SecurityAgent::noReason) {
198 return true;
199 }
200 }
201 else {
202 // another thread already unlocked the keybag
203 return true;
204 }
205 break;
206 }
207 // try to use an explicitly given passphrase - Data:passphrase
208 case CSSM_SAMPLE_TYPE_PASSWORD: {
209 if (sample.length() != 2)
210 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
211 secinfo("KCdb", "attempting passphrase unlock of keybag");
212 if (unlock_keybag(db, sample[1].data().data(), (int)sample[1].data().length())) {
213 return true;
214 }
215 break;
216 }
217 default: {
218 // Unknown sub-sample for unlocking.
219 secinfo("KCdb", "keybag: unknown sub-sample unlock (%d) ignored", sample.type());
220 break;
221 }
222 }
223 }
224 }
225 return false;
226 }
227
228 //
229 // Create a Database object from initial parameters (create operation)
230 //
231 KeychainDatabase::KeychainDatabase(const DLDbIdentifier &id, const DBParameters &params, Process &proc,
232 const AccessCredentials *cred, const AclEntryPrototype *owner)
233 : LocalDatabase(proc), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive)), mSaveSecret(false), version(0), mBlob(NULL), mRecoded(false)
234 {
235 // save a copy of the credentials for later access control
236 mCred = DataWalkers::copy(cred, Allocator::standard());
237
238 // create a new random signature to complete the DLDbIdentifier
239 DbBlob::Signature newSig;
240 Server::active().random(newSig.bytes);
241 DbIdentifier ident(id, newSig);
242
243 // create common block and initialize
244 // Since this is a creation step, figure out the correct blob version for this database
245 RefPointer<KeychainDbCommon> newCommon = new KeychainDbCommon(proc.session(), ident, CommonBlob::getCurrentVersionForDb(ident.dbName()));
246 newCommon->initializeKeybag();
247
248 StLock<Mutex> _(*newCommon);
249 parent(*newCommon);
250 newCommon->insert();
251 // new common is now visible (in ident-map) but we hold its lock
252
253 // establish the new master secret
254 establishNewSecrets(cred, SecurityAgent::newDatabase);
255
256 // set initial database parameters
257 common().mParams = params;
258
259 // the common is "unlocked" now
260 common().makeNewSecrets();
261
262 // establish initial ACL
263 if (owner)
264 acl().cssmSetInitial(*owner);
265 else
266 acl().cssmSetInitial(new AnyAclSubject());
267 mValidData = true;
268
269 // for now, create the blob immediately
270 encode();
271
272 proc.addReference(*this);
273
274 // this new keychain is unlocked; make it so
275 activity();
276
277 secinfo("KCdb", "creating keychain %p %s with common %p", this, (char*)this->dbName(), &common());
278 }
279
280
281 //
282 // Create a Database object from a database blob (decoding)
283 //
284 KeychainDatabase::KeychainDatabase(const DLDbIdentifier &id, const DbBlob *blob, Process &proc,
285 const AccessCredentials *cred)
286 : LocalDatabase(proc), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive)), mSaveSecret(false), version(0), mBlob(NULL), mRecoded(false)
287 {
288 validateBlob(blob);
289
290 // save a copy of the credentials for later access control
291 mCred = DataWalkers::copy(cred, Allocator::standard());
292 mBlob = blob->copy();
293
294 // check to see if we already know about this database
295 DbIdentifier ident(id, blob->randomSignature);
296 Session &session = process().session();
297 RefPointer<KeychainDbCommon> com;
298 secnotice("kccommon", "looking for a common at %s", ident.dbName());
299 if (KeychainDbCommon::find(ident, session, com)) {
300 secnotice("kccommon", "found %p", com.get());
301 parent(*com);
302 secinfo("KCdb", "joining keychain %p %s with common %p", this, (char*)this->dbName(), &common());
303 } else {
304 // DbCommon not present; make a new one
305 secnotice("kccommon", "no common found");
306 parent(*com);
307 common().mParams = blob->params;
308 secinfo("KCdb", "making keychain %p %s with common %p", this, (char*)this->dbName(), &common());
309 // this DbCommon is locked; no timer or reference setting
310 }
311 proc.addReference(*this);
312 }
313
314 void KeychainDbCommon::insert()
315 {
316 StReadWriteLock _(mRWCommonLock, StReadWriteLock::Write);
317 insertHoldingLock();
318 }
319
320 void KeychainDbCommon::insertHoldingLock()
321 {
322 mCommonSet.insert(this);
323 }
324
325
326
327 // find or make a DbCommon. Returns true if an existing one was found and used.
328 bool KeychainDbCommon::find(const DbIdentifier &ident, Session &session, RefPointer<KeychainDbCommon> &common, uint32 requestedVersion, KeychainDbCommon* cloneFrom)
329 {
330 // Prepare to drop the mRWCommonLock.
331 {
332 StReadWriteLock _(mRWCommonLock, StReadWriteLock::Read);
333 for (CommonSet::const_iterator it = mCommonSet.begin(); it != mCommonSet.end(); ++it) {
334 if (&session == &(*it)->session() && ident == (*it)->identifier()) {
335 common = *it;
336 secnotice("kccommon", "found a common for %s at %p", ident.dbName(), common.get());
337 return true;
338 }
339 }
340 }
341
342 // not found. Grab the write lock, ensure that nobody has beaten us to adding,
343 // and then create a DbCommon and add it to the map.
344 {
345 StReadWriteLock _(mRWCommonLock, StReadWriteLock::Write);
346 for (CommonSet::const_iterator it = mCommonSet.begin(); it != mCommonSet.end(); ++it) {
347 if (&session == &(*it)->session() && ident == (*it)->identifier()) {
348 common = *it;
349 secnotice("kccommon", "found a common for %s at %p", ident.dbName(), common.get());
350 return true;
351 }
352 }
353
354 // not found
355 if(cloneFrom) {
356 common = new KeychainDbCommon(session, ident, *cloneFrom);
357 } else if(requestedVersion != CommonBlob::version_none) {
358 common = new KeychainDbCommon(session, ident, requestedVersion);
359 } else {
360 common = new KeychainDbCommon(session, ident);
361 }
362
363 secnotice("kccommon", "made a new common for %s at %p", ident.dbName(), common.get());
364
365 // Can't call insert() here, because it grabs the write lock (which we have).
366 common->insertHoldingLock();
367 }
368 common->initializeKeybag();
369 return false;
370 }
371
372 // recode/clone:
373 //
374 // Special-purpose constructor for keychain synchronization. Copies an
375 // existing keychain but uses the operational keys from secretsBlob. The
376 // new KeychainDatabase will silently replace the existing KeychainDatabase
377 // as soon as the client declares that re-encoding of all keychain items is
378 // finished. This is a little perilous since it allows a client to dictate
379 // securityd state, but we try to ensure that only the client that started
380 // the re-encoding can declare it done.
381 //
382 KeychainDatabase::KeychainDatabase(KeychainDatabase &src, Process &proc, DbHandle dbToClone)
383 : LocalDatabase(proc), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive)), mSaveSecret(false), version(0), mBlob(NULL), mRecoded(false)
384 {
385 mCred = DataWalkers::copy(src.mCred, Allocator::standard());
386
387 // Give this KeychainDatabase a temporary name
388 std::string newDbName = std::string("////") + std::string(src.identifier().dbName());
389 DLDbIdentifier newDLDbIdent(src.identifier().dlDbIdentifier().ssuid(), newDbName.c_str(), src.identifier().dlDbIdentifier().dbLocation());
390 DbIdentifier ident(newDLDbIdent, src.identifier());
391
392 // create common block and initialize
393 RefPointer<KeychainDbCommon> newCommon = new KeychainDbCommon(proc.session(), ident);
394 newCommon->initializeKeybag();
395 StLock<Mutex> _(*newCommon);
396 parent(*newCommon);
397 newCommon->insert();
398
399 // set initial database parameters from the source keychain
400 common().mParams = src.common().mParams;
401
402 // establish the source keychain's master secret as ours
403 // @@@ NB: this is a v. 0.1 assumption. We *should* trigger new UI
404 // that offers the user the option of using the existing password
405 // or choosing a new one. That would require a new
406 // SecurityAgentQuery type, new UI, and--possibly--modifications to
407 // ensure that the new password is available here to generate the
408 // new master secret.
409 src.unlockDb(false); // precaution for masterKey()
410 common().setup(src.blob(), src.common().masterKey());
411
412 // import the operational secrets
413 RefPointer<KeychainDatabase> srcKC = Server::keychain(dbToClone);
414 common().importSecrets(srcKC->common());
415
416 // import source keychain's ACL
417 CssmData pubAcl, privAcl;
418 src.acl().exportBlob(pubAcl, privAcl);
419 importBlob(pubAcl.data(), privAcl.data());
420 src.acl().allocator.free(pubAcl);
421 src.acl().allocator.free(privAcl);
422
423 // indicate that this keychain should be allowed to do some otherwise
424 // risky things required for copying, like re-encoding keys
425 mRecodingSource = &src;
426
427 common().setUnlocked();
428 mValidData = true;
429
430 encode();
431
432 proc.addReference(*this);
433 secinfo("SSdb", "database %s(%p) created as copy, common at %p",
434 common().dbName(), this, &common());
435 }
436
437 // Make a new KeychainDatabase from an old one, but have a completely different location
438 KeychainDatabase::KeychainDatabase(const DLDbIdentifier& id, KeychainDatabase &src, Process &proc)
439 : LocalDatabase(proc), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive)), mSaveSecret(false), version(0), mBlob(NULL), mRecoded(false)
440 {
441 mCred = DataWalkers::copy(src.mCred, Allocator::standard());
442
443 DbIdentifier ident(id, src.identifier());
444
445 // create common block and initialize
446 RefPointer<KeychainDbCommon> newCommon;
447 if(KeychainDbCommon::find(ident, process().session(), newCommon, CommonBlob::version_none, &src.common())) {
448 // A common already existed. Write over it, but note that everything may go horribly from here on out.
449 secnotice("kccommon", "Found common where we didn't expect. Possible strange behavior ahead.");
450 newCommon->cloneFrom(src.common());
451 }
452
453 StLock<Mutex> _(*newCommon);
454 parent(*newCommon);
455
456 // set initial database parameters from the source keychain
457 common().mParams = src.common().mParams;
458
459 // import source keychain's ACL
460 CssmData pubAcl, privAcl;
461 src.acl().exportBlob(pubAcl, privAcl);
462 importBlob(pubAcl.data(), privAcl.data());
463 src.acl().allocator.free(pubAcl);
464 src.acl().allocator.free(privAcl);
465
466 // Copy the source database's blob, if possible
467 if(src.mBlob) {
468 mBlob = src.mBlob->copy();
469 version = src.version;
470 }
471
472 // We've copied everything we can from our source. If they were valid, so are we.
473 mValidData = src.mValidData;
474
475 proc.addReference(*this);
476 secinfo("SSdb", "database %s(%p) created as expected clone, common at %p", common().dbName(), this, &common());
477 }
478
479
480 // Make a new KeychainDatabase from an old one, but have entirely new operational secrets
481 KeychainDatabase::KeychainDatabase(uint32 requestedVersion, KeychainDatabase &src, Process &proc)
482 : LocalDatabase(proc), mValidData(false), mSecret(Allocator::standard(Allocator::sensitive)), mSaveSecret(false), version(0), mBlob(NULL), mRecoded(false)
483 {
484 mCred = DataWalkers::copy(src.mCred, Allocator::standard());
485
486 // Give this KeychainDatabase a temporary name
487 // this must canonicalize to a different path than the original DB, otherwise another process opening the existing DB wil find this new KeychainDbCommon
488 // and call decodeCore with the old blob, overwriting the new secrets and wreaking havoc
489 std::string newDbName = std::string("////") + std::string(src.identifier().dbName()) + std::string("_com.apple.security.keychain.migrating");
490 DLDbIdentifier newDLDbIdent(src.identifier().dlDbIdentifier().ssuid(), newDbName.c_str(), src.identifier().dlDbIdentifier().dbLocation());
491 DbIdentifier ident(newDLDbIdent, src.identifier());
492
493 // hold the lock for src's common during this operation (to match locking common locking order with KeychainDatabase::recodeKey)
494 StLock<Mutex> __(src.common());
495
496 // create common block and initialize
497 RefPointer<KeychainDbCommon> newCommon;
498 if(KeychainDbCommon::find(ident, process().session(), newCommon, requestedVersion)) {
499 // A common already existed here. Write over it, but note that everything may go horribly from here on out.
500 secnotice("kccommon", "Found common where we didn't expect. Possible strange behavior ahead.");
501 newCommon->cloneFrom(src.common(), requestedVersion);
502 }
503 newCommon->initializeKeybag();
504 StLock<Mutex> _(*newCommon);
505 parent(*newCommon);
506
507 // We want to re-use the master secrets from the source database (and so the
508 // same password), but reroll new operational secrets.
509
510 // Copy the master secret over...
511 src.unlockDb(false); // precaution
512
513 common().setup(src.blob(), src.common().masterKey(), false); // keep the new common's version intact
514
515 // set initial database parameters from the source keychain
516 common().mParams = src.common().mParams;
517
518 // and make new operational secrets
519 common().makeNewSecrets();
520
521 // import source keychain's ACL
522 CssmData pubAcl, privAcl;
523 src.acl().exportBlob(pubAcl, privAcl);
524 importBlob(pubAcl.data(), privAcl.data());
525 src.acl().allocator.free(pubAcl);
526 src.acl().allocator.free(privAcl);
527
528 // indicate that this keychain should be allowed to do some otherwise
529 // risky things required for copying, like re-encoding keys
530 mRecodingSource = &src;
531
532 common().setUnlocked();
533 mValidData = true;
534
535 encode();
536
537 proc.addReference(*this);
538 secinfo("SSdb", "database %s(%p) created as expected copy, common at %p",
539 common().dbName(), this, &common());
540 }
541
542 //
543 // Destroy a Database
544 //
545 KeychainDatabase::~KeychainDatabase()
546 {
547 secinfo("KCdb", "deleting database %s(%p) common %p",
548 common().dbName(), this, &common());
549 Allocator::standard().free(mCred);
550 Allocator::standard().free(mBlob);
551 }
552
553
554 //
555 // Basic Database virtual implementations
556 //
557 KeychainDbCommon &KeychainDatabase::common() const
558 {
559 return parent<KeychainDbCommon>();
560 }
561
562 const char *KeychainDatabase::dbName() const
563 {
564 return common().dbName();
565 }
566
567 bool KeychainDatabase::transient() const
568 {
569 return false; // has permanent store
570 }
571
572 AclKind KeychainDatabase::aclKind() const
573 {
574 return dbAcl;
575 }
576
577 Database *KeychainDatabase::relatedDatabase()
578 {
579 return this;
580 }
581
582 //
583 // (Re-)Authenticate the database. This changes the stored credentials.
584 //
585 void KeychainDatabase::authenticate(CSSM_DB_ACCESS_TYPE mode,
586 const AccessCredentials *cred)
587 {
588 StLock<Mutex> _(common());
589
590 // the (Apple specific) RESET bit means "lock the database now"
591 switch (mode) {
592 case CSSM_DB_ACCESS_RESET:
593 secinfo("KCdb", "%p ACCESS_RESET triggers keychain lock", this);
594 common().lockDb();
595 break;
596 default:
597 // store the new credentials for future use
598 secinfo("KCdb", "%p authenticate stores new database credentials", this);
599 AccessCredentials *newCred = DataWalkers::copy(cred, Allocator::standard());
600 Allocator::standard().free(mCred);
601 mCred = newCred;
602 }
603 }
604
605
606 //
607 // Make a new KeychainKey.
608 // If PERMANENT is off, make a temporary key instead.
609 // The db argument allows you to create for another KeychainDatabase (only);
610 // it defaults to ourselves.
611 //
612 RefPointer<Key> KeychainDatabase::makeKey(Database &db, const CssmKey &newKey,
613 uint32 moreAttributes, const AclEntryPrototype *owner)
614 {
615
616 if (moreAttributes & CSSM_KEYATTR_PERMANENT)
617 return new KeychainKey(db, newKey, moreAttributes, owner);
618 else
619 return process().makeTemporaryKey(newKey, moreAttributes, owner);
620 }
621
622 RefPointer<Key> KeychainDatabase::makeKey(const CssmKey &newKey,
623 uint32 moreAttributes, const AclEntryPrototype *owner)
624 {
625 return makeKey(*this, newKey, moreAttributes, owner);
626 }
627
628
629 //
630 // Return the database blob, recalculating it as needed.
631 //
632 DbBlob *KeychainDatabase::blob()
633 {
634 StLock<Mutex> _(common());
635 if (!validBlob()) {
636 makeUnlocked(false); // unlock to get master secret
637 encode(); // (re)encode blob if needed
638 }
639 activity(); // reset timeout
640 assert(validBlob()); // better have a valid blob now...
641 return mBlob;
642 }
643
644
645 //
646 // Encode the current database as a blob.
647 // Note that this returns memory we own and keep.
648 // Caller must hold common lock.
649 //
650 void KeychainDatabase::encode()
651 {
652 DbBlob *blob = common().encode(*this);
653 Allocator::standard().free(mBlob);
654 mBlob = blob;
655 version = common().version;
656 secinfo("KCdb", "encoded database %p common %p(%s) version %u params=(%u,%u)",
657 this, &common(), dbName(), version,
658 common().mParams.idleTimeout, common().mParams.lockOnSleep);
659 }
660
661
662 //
663 // Change the passphrase on a database
664 //
665 void KeychainDatabase::changePassphrase(const AccessCredentials *cred)
666 {
667 // get and hold the common lock (don't let other threads break in here)
668 StLock<Mutex> _(common());
669
670 // establish OLD secret - i.e. unlock the database
671 //@@@ do we want to leave the final lock state alone?
672 if (common().isLoginKeychain()) mSaveSecret = true;
673 makeUnlocked(cred, false);
674
675 // establish NEW secret
676 if(!establishNewSecrets(cred, SecurityAgent::changePassphrase)) {
677 secinfo("KCdb", "Old and new passphrases are the same. Database %s(%p) master secret did not change.",
678 common().dbName(), this);
679 return;
680 }
681 if (mSecret) { mSecret.reset(); }
682 mSaveSecret = false;
683 common().invalidateBlob(); // blob state changed
684 secinfo("KCdb", "Database %s(%p) master secret changed", common().dbName(), this);
685 encode(); // force rebuild of local blob
686
687 // send out a notification
688 notify(kNotificationEventPassphraseChanged);
689
690 // I guess this counts as an activity
691 activity();
692 }
693
694 //
695 // Second stage of keychain synchronization: overwrite the original keychain's
696 // (this KeychainDatabase's) operational secrets
697 //
698 void KeychainDatabase::commitSecretsForSync(KeychainDatabase &cloneDb)
699 {
700 StLock<Mutex> _(common());
701
702 // try to detect spoofing
703 if (cloneDb.mRecodingSource != this)
704 CssmError::throwMe(CSSM_ERRCODE_INVALID_DB_HANDLE);
705
706 // in case we autolocked since starting the sync
707 makeUnlocked(false); // call this because we already own the lock
708 cloneDb.unlockDb(false); // we may not own the lock here, so calling unlockDb will lock the cloneDb's common lock
709
710 // Decode all keys whose handles refer to this on-disk keychain so that
711 // if the holding client commits the key back to disk, it's encoded with
712 // the new operational secrets. The recoding client *must* hold a write
713 // lock for the on-disk keychain from the moment it starts recoding key
714 // items until after this call.
715 //
716 // @@@ This specific implementation is a workaround for 4003540.
717 std::vector<U32HandleObject::Handle> handleList;
718 U32HandleObject::findAllRefs<KeychainKey>(handleList);
719 size_t count = handleList.size();
720 if (count > 0) {
721 for (unsigned int n = 0; n < count; ++n) {
722 RefPointer<KeychainKey> kckey =
723 U32HandleObject::findRefAndLock<KeychainKey>(handleList[n], CSSMERR_CSP_INVALID_KEY_REFERENCE);
724 StLock<Mutex> _(*kckey/*, true*/);
725 if (kckey->database().global().identifier() == identifier()) {
726 kckey->key(); // force decode
727 kckey->invalidateBlob();
728 secinfo("kcrecode", "changed extant key %p (proc %d)",
729 &*kckey, kckey->process().pid());
730 }
731 }
732 }
733
734 // mark down that we just recoded
735 mRecoded = true;
736
737 // it is now safe to replace the old op secrets
738 common().importSecrets(cloneDb.common());
739 common().invalidateBlob();
740 }
741
742
743 //
744 // Extract the database master key as a proper Key object.
745 //
746 RefPointer<Key> KeychainDatabase::extractMasterKey(Database &db,
747 const AccessCredentials *cred, const AclEntryPrototype *owner,
748 uint32 usage, uint32 attrs)
749 {
750 // get and hold common lock
751 StLock<Mutex> _(common());
752
753 // force lock to require re-validation of credentials
754 lockDb();
755
756 // unlock to establish master secret
757 makeUnlocked(false);
758
759 // extract the raw cryptographic key
760 CssmClient::WrapKey wrap(Server::csp(), CSSM_ALGID_NONE);
761 CssmKey key;
762 wrap(common().masterKey(), key);
763
764 // make the key object and return it
765 return makeKey(db, key, attrs & LocalKey::managedAttributes, owner);
766 }
767
768
769 //
770 // Unlock this database (if needed) by obtaining the master secret in some
771 // suitable way and then proceeding to unlock with it.
772 // Does absolutely nothing if the database is already unlocked.
773 // The makeUnlocked forms are identical except the assume the caller already
774 // holds the common lock.
775 //
776 void KeychainDatabase::unlockDb(bool unlockKeybag)
777 {
778 StLock<Mutex> _(common());
779 makeUnlocked(unlockKeybag);
780 }
781
782 void KeychainDatabase::makeUnlocked(bool unlockKeybag)
783 {
784 return makeUnlocked(mCred, unlockKeybag);
785 }
786
787 void KeychainDatabase::makeUnlocked(const AccessCredentials *cred, bool unlockKeybag)
788 {
789 if (isLocked()) {
790 secinfo("KCdb", "%p(%p) unlocking for makeUnlocked()", this, &common());
791 assert(mBlob || (mValidData && common().hasMaster()));
792 establishOldSecrets(cred);
793 common().setUnlocked(); // mark unlocked
794 if (common().isLoginKeychain()) {
795 CssmKey master = common().masterKey();
796 CssmKey rawMaster;
797 CssmClient::WrapKey wrap(Server::csp(), CSSM_ALGID_NONE);
798 wrap(master, rawMaster);
799
800 service_context_t context = common().session().get_current_service_context();
801 service_client_stash_load_key(&context, rawMaster.keyData(), (int)rawMaster.length());
802 }
803 } else if (unlockKeybag && common().isLoginKeychain()) {
804 bool locked = false;
805 service_context_t context = common().session().get_current_service_context();
806 if ((service_client_kb_is_locked(&context, &locked, NULL) == 0) && locked) {
807 if (!unlock_keybag_with_cred(*this, cred)) {
808 syslog(LOG_NOTICE, "failed to unlock iCloud keychain");
809 }
810 }
811 }
812 if (!mValidData) { // need to decode to get our ACLs, master secret available
813 secinfo("KCdb", "%p(%p) is unlocked; decoding for makeUnlocked()", this, &common());
814 if (!decode())
815 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
816 }
817 assert(!isLocked());
818 assert(mValidData);
819 }
820
821 //
822 // Invoke the securityd_service to retrieve the keychain master
823 // key from the AppleFDEKeyStore.
824 //
825 void KeychainDatabase::stashDbCheck()
826 {
827 CssmAutoData masterKey(Allocator::standard(Allocator::sensitive));
828 CssmAutoData encKey(Allocator::standard(Allocator::sensitive));
829
830 // Fetch the key
831 int rc = 0;
832 void * stash_key = NULL;
833 int stash_key_len = 0;
834 service_context_t context = common().session().get_current_service_context();
835 rc = service_client_stash_get_key(&context, &stash_key, &stash_key_len);
836 if (rc == 0) {
837 if (stash_key) {
838 masterKey.copy(CssmData((void *)stash_key,stash_key_len));
839 memset(stash_key, 0, stash_key_len);
840 free(stash_key);
841 }
842 } else {
843 CssmError::throwMe(rc);
844 }
845
846 {
847 StLock<Mutex> _(common());
848
849 // Now establish it as the keychain master key
850 CssmClient::Key key(Server::csp(), masterKey.get());
851 CssmKey::Header &hdr = key.header();
852 hdr.keyClass(CSSM_KEYCLASS_SESSION_KEY);
853 hdr.algorithm(CSSM_ALGID_3DES_3KEY_EDE);
854 hdr.usage(CSSM_KEYUSE_ANY);
855 hdr.blobType(CSSM_KEYBLOB_RAW);
856 hdr.blobFormat(CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING);
857 common().setup(mBlob, key);
858
859 if (!decode())
860 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
861
862 common().get_encryption_key(encKey);
863 }
864
865 // when upgrading from pre-10.9 create a keybag if it doesn't exist with the encryption key
866 // only do this after we have verified the master key unlocks the login.keychain
867 if (service_client_kb_load(&context) == KB_BagNotFound) {
868 service_client_kb_create(&context, encKey.data(), (int)encKey.length());
869 }
870 }
871
872 //
873 // Get the keychain master key and invoke the securityd_service
874 // to stash it in the AppleFDEKeyStore ready for commit to the
875 // NVRAM blob.
876 //
877 void KeychainDatabase::stashDb()
878 {
879 CssmAutoData data(Allocator::standard(Allocator::sensitive));
880
881 {
882 StLock<Mutex> _(common());
883
884 if (!common().isValid()) {
885 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
886 }
887
888 CssmKey key = common().masterKey();
889 data.copy(key.keyData());
890 }
891
892 service_context_t context = common().session().get_current_service_context();
893 int rc = service_client_stash_set_key(&context, data.data(), (int)data.length());
894 if (rc != 0) CssmError::throwMe(rc);
895 }
896
897 //
898 // The following unlock given an explicit passphrase, rather than using
899 // (special cred sample based) default procedures.
900 //
901 void KeychainDatabase::unlockDb(const CssmData &passphrase, bool unlockKeybag)
902 {
903 StLock<Mutex> _(common());
904 makeUnlocked(passphrase, unlockKeybag);
905 }
906
907 void KeychainDatabase::makeUnlocked(const CssmData &passphrase, bool unlockKeybag)
908 {
909 if (isLocked()) {
910 if (decode(passphrase))
911 return;
912 else
913 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
914 } else if (!mValidData) { // need to decode to get our ACLs, passphrase available
915 if (!decode())
916 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
917 }
918
919 if (unlockKeybag && common().isLoginKeychain()) {
920 bool locked = false;
921 service_context_t context = common().session().get_current_service_context();
922 if (!common().session().keybagGetState(session_keybag_check_master_key) || ((service_client_kb_is_locked(&context, &locked, NULL) == 0) && locked)) {
923 unlock_keybag(*this, passphrase.data(), (int)passphrase.length());
924 }
925 }
926
927 assert(!isLocked());
928 assert(mValidData);
929 }
930
931
932 //
933 // Nonthrowing passphrase-based unlock. This returns false if unlock failed.
934 // Note that this requires an explicitly given passphrase.
935 // Caller must hold common lock.
936 //
937 bool KeychainDatabase::decode(const CssmData &passphrase)
938 {
939 assert(mBlob);
940 common().setup(mBlob, passphrase);
941 bool success = decode();
942 if (success && common().isLoginKeychain()) {
943 unlock_keybag(*this, passphrase.data(), (int)passphrase.length());
944 }
945 return success;
946 }
947
948
949 //
950 // Given the established master secret, decode the working keys and other
951 // functional secrets for this database. Return false (do NOT throw) if
952 // the decode fails. Call this in low(er) level code once you established
953 // the master key.
954 //
955 bool KeychainDatabase::decode()
956 {
957 assert(mBlob);
958 assert(common().hasMaster());
959 void *privateAclBlob;
960 if (common().unlockDb(mBlob, &privateAclBlob)) {
961 if (!mValidData) {
962 acl().importBlob(mBlob->publicAclBlob(), privateAclBlob);
963 mValidData = true;
964 }
965 Allocator::standard().free(privateAclBlob);
966 return true;
967 }
968 secinfo("KCdb", "%p decode failed", this);
969 return false;
970 }
971
972
973 //
974 // Given an AccessCredentials for this database, wring out the existing primary
975 // database secret by whatever means necessary.
976 // On entry, caller must hold the database common lock. It will be held
977 // throughout except when user interaction is required. User interaction
978 // requires relinquishing the database common lock and taking the UI lock. On
979 // return from user interaction, the UI lock is relinquished and the database
980 // common lock must be reacquired. At no time may the caller hold both locks.
981 // On exit, the crypto core has its master secret. If things go wrong,
982 // we will throw a suitable exception. Note that encountering any malformed
983 // credential sample will throw, but this is not guaranteed -- don't assume
984 // that NOT throwing means creds is entirely well-formed (it may just be good
985 // enough to work THIS time).
986 //
987 // How this works:
988 // Walk through the creds. Fish out those credentials (in order) that
989 // are for unlock processing (they have no ACL subject correspondents),
990 // and (try to) obey each in turn, until one produces a valid secret
991 // or you run out. If no special samples are found at all, interpret that as
992 // "use the system global default," which happens to be hard-coded right here.
993 //
994 void KeychainDatabase::establishOldSecrets(const AccessCredentials *creds)
995 {
996 bool forSystem = this->belongsToSystem(); // this keychain belongs to the system security domain
997
998 // attempt system-keychain unlock
999 if (forSystem) {
1000 SystemKeychainKey systemKeychain(kSystemUnlockFile);
1001 if (systemKeychain.matches(mBlob->randomSignature)) {
1002 secinfo("KCdb", "%p attempting system unlock", this);
1003 common().setup(mBlob, CssmClient::Key(Server::csp(), systemKeychain.key(), true));
1004 if (decode())
1005 return;
1006 }
1007 }
1008
1009 list<CssmSample> samples;
1010 if (creds && creds->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, samples)) {
1011 for (list<CssmSample>::iterator it = samples.begin(); it != samples.end(); it++) {
1012 TypedList &sample = *it;
1013 sample.checkProper();
1014 switch (sample.type()) {
1015 // interactively prompt the user - no additional data
1016 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT:
1017 if (!forSystem) {
1018 if (interactiveUnlock())
1019 return;
1020 }
1021 break;
1022 // try to use an explicitly given passphrase - Data:passphrase
1023 case CSSM_SAMPLE_TYPE_PASSWORD:
1024 if (sample.length() != 2)
1025 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
1026 secinfo("KCdb", "%p attempting passphrase unlock", this);
1027 if (decode(sample[1]))
1028 return;
1029 break;
1030 // try to open with a given master key - Data:CSP or KeyHandle, Data:CssmKey
1031 case CSSM_SAMPLE_TYPE_SYMMETRIC_KEY:
1032 case CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY:
1033 assert(mBlob);
1034 secinfo("KCdb", "%p attempting explicit key unlock", this);
1035 common().setup(mBlob, keyFromCreds(sample, 4));
1036 if (decode()) {
1037 return;
1038 }
1039 break;
1040 // explicitly defeat the default action but don't try anything in particular
1041 case CSSM_WORDID_CANCELED:
1042 secinfo("KCdb", "%p defeat default action", this);
1043 break;
1044 default:
1045 // Unknown sub-sample for unlocking.
1046 // If we wanted to be fascist, we could now do
1047 // CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
1048 // But instead we try to be tolerant and continue on.
1049 // This DOES however count as an explicit attempt at specifying unlock,
1050 // so we will no longer try the default case below...
1051 secinfo("KCdb", "%p unknown sub-sample unlock (%d) ignored", this, sample.type());
1052 break;
1053 }
1054 }
1055 } else {
1056 // default action
1057 assert(mBlob);
1058
1059 if (!forSystem) {
1060 if (interactiveUnlock())
1061 return;
1062 }
1063 }
1064
1065 // out of options - no secret obtained
1066 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
1067 }
1068
1069 //
1070 // This function is almost identical to establishOldSecrets, but:
1071 // 1. It will never prompt the user; these credentials either work or they don't
1072 // 2. It will not change the secrets of this database
1073 //
1074 // TODO: These two functions should probably be refactored to something nicer.
1075 bool KeychainDatabase::checkCredentials(const AccessCredentials *creds) {
1076
1077 list<CssmSample> samples;
1078 if (creds && creds->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, samples)) {
1079 for (list<CssmSample>::iterator it = samples.begin(); it != samples.end(); it++) {
1080 TypedList &sample = *it;
1081 sample.checkProper();
1082 switch (sample.type()) {
1083 // interactively prompt the user - no additional data
1084 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT:
1085 // do nothing, because this function will never prompt the user
1086 secinfo("integrity", "%p ignoring keychain prompt", this);
1087 break;
1088 // try to use an explicitly given passphrase - Data:passphrase
1089 case CSSM_SAMPLE_TYPE_PASSWORD:
1090 if (sample.length() != 2)
1091 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
1092 secinfo("integrity", "%p checking passphrase", this);
1093 if(validatePassphrase(sample[1])) {
1094 return true;
1095 }
1096 break;
1097 // try to open with a given master key - Data:CSP or KeyHandle, Data:CssmKey
1098 case CSSM_SAMPLE_TYPE_SYMMETRIC_KEY:
1099 case CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY:
1100 assert(mBlob);
1101 secinfo("integrity", "%p attempting explicit key unlock", this);
1102 try {
1103 CssmClient::Key checkKey = keyFromCreds(sample, 4);
1104 if(common().validateKey(checkKey)) {
1105 return true;
1106 }
1107 } catch(...) {
1108 // ignore all problems in keyFromCreds
1109 secinfo("integrity", "%p caught error", this);
1110 }
1111 break;
1112 }
1113 }
1114 }
1115
1116 // out of options - credentials don't match
1117 return false;
1118 }
1119
1120 uint32_t KeychainDatabase::interactiveUnlockAttempts = 0;
1121
1122 bool KeychainDatabase::interactiveUnlock()
1123 {
1124 secinfo("KCdb", "%p attempting interactive unlock", this);
1125 interactiveUnlockAttempts++;
1126
1127 SecurityAgent::Reason reason = SecurityAgent::noReason;
1128 QueryUnlock query(*this);
1129 // take UI interlock and release DbCommon lock (to avoid deadlocks)
1130 StSyncLock<Mutex, Mutex> uisync(common().uiLock(), common());
1131
1132 // now that we have the UI lock, interact unless another thread unlocked us first
1133 if (isLocked()) {
1134 query.inferHints(Server::process());
1135 reason = query();
1136 if (mSaveSecret && reason == SecurityAgent::noReason) {
1137 query.retrievePassword(mSecret);
1138 }
1139 query.disconnect();
1140 } else {
1141 secinfo("KCdb", "%p was unlocked during uiLock delay", this);
1142 }
1143
1144 if (common().isLoginKeychain()) {
1145 bool locked = false;
1146 service_context_t context = common().session().get_current_service_context();
1147 if ((service_client_kb_is_locked(&context, &locked, NULL) == 0) && locked) {
1148 QueryKeybagNewPassphrase keybagQuery(common().session());
1149 keybagQuery.inferHints(Server::process());
1150 CssmAutoData pass(Allocator::standard(Allocator::sensitive));
1151 CssmAutoData oldPass(Allocator::standard(Allocator::sensitive));
1152 SecurityAgent::Reason queryReason = keybagQuery.query(oldPass, pass);
1153 if (queryReason == SecurityAgent::noReason) {
1154 service_client_kb_change_secret(&context, oldPass.data(), (int)oldPass.length(), pass.data(), (int)pass.length());
1155 } else if (queryReason == SecurityAgent::resettingPassword) {
1156 query.retrievePassword(pass);
1157 service_client_kb_reset(&context, pass.data(), (int)pass.length());
1158 }
1159
1160 }
1161 }
1162
1163 return reason == SecurityAgent::noReason;
1164 }
1165
1166 uint32_t KeychainDatabase::getInteractiveUnlockAttempts() {
1167 if (csr_check(CSR_ALLOW_APPLE_INTERNAL)) {
1168 // Not an internal install; don't answer
1169 return 0;
1170 } else {
1171 return interactiveUnlockAttempts;
1172 }
1173 }
1174
1175
1176 //
1177 // Same thing, but obtain a new secret somehow and set it into the common.
1178 //
1179 bool KeychainDatabase::establishNewSecrets(const AccessCredentials *creds, SecurityAgent::Reason reason)
1180 {
1181 list<CssmSample> samples;
1182 if (creds && creds->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK, samples)) {
1183 for (list<CssmSample>::iterator it = samples.begin(); it != samples.end(); it++) {
1184 TypedList &sample = *it;
1185 sample.checkProper();
1186 switch (sample.type()) {
1187 // interactively prompt the user
1188 case CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT:
1189 {
1190 secinfo("KCdb", "%p specified interactive passphrase", this);
1191 QueryNewPassphrase query(*this, reason);
1192 StSyncLock<Mutex, Mutex> uisync(common().uiLock(), common());
1193 query.inferHints(Server::process());
1194 CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
1195 CssmAutoData oldPassphrase(Allocator::standard(Allocator::sensitive));
1196 if (query(oldPassphrase, passphrase) == SecurityAgent::noReason) {
1197 common().setup(NULL, passphrase);
1198 change_secret_on_keybag(*this, oldPassphrase.data(), (int)oldPassphrase.length(), passphrase.data(), (int)passphrase.length());
1199 return true;
1200 }
1201 }
1202 break;
1203 // try to use an explicitly given passphrase
1204 case CSSM_SAMPLE_TYPE_PASSWORD:
1205 {
1206 secinfo("KCdb", "%p specified explicit passphrase", this);
1207 if (sample.length() != 2)
1208 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
1209 if (common().isLoginKeychain()) {
1210 CssmAutoData oldPassphrase(Allocator::standard(Allocator::sensitive));
1211 list<CssmSample> oldSamples;
1212 creds->samples().collect(CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, oldSamples);
1213 for (list<CssmSample>::iterator oit = oldSamples.begin(); oit != oldSamples.end(); oit++) {
1214 TypedList &tmpList = *oit;
1215 tmpList.checkProper();
1216 if (tmpList.type() == CSSM_SAMPLE_TYPE_PASSWORD) {
1217 if (tmpList.length() == 2) {
1218 oldPassphrase = tmpList[1].data();
1219 }
1220 }
1221 }
1222 if (!oldPassphrase.length() && mSecret && mSecret.length()) {
1223 oldPassphrase = mSecret;
1224 }
1225 if ((oldPassphrase.length() == sample[1].data().length()) &&
1226 !memcmp(oldPassphrase.data(), sample[1].data().data(), oldPassphrase.length()) &&
1227 oldPassphrase.length()) {
1228 // don't change master key if the passwords are the same
1229 return false;
1230 }
1231 common().setup(NULL, sample[1]);
1232 change_secret_on_keybag(*this, oldPassphrase.data(), (int)oldPassphrase.length(), sample[1].data().data(), (int)sample[1].data().length());
1233 }
1234 else {
1235 common().setup(NULL, sample[1]);
1236 }
1237 return true;
1238 }
1239 // try to open with a given master key
1240 case CSSM_WORDID_SYMMETRIC_KEY:
1241 case CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY:
1242 secinfo("KCdb", "%p specified explicit master key", this);
1243 common().setup(NULL, keyFromCreds(sample, 3));
1244 return true;
1245 // explicitly defeat the default action but don't try anything in particular
1246 case CSSM_WORDID_CANCELED:
1247 secinfo("KCdb", "%p defeat default action", this);
1248 break;
1249 default:
1250 // Unknown sub-sample for acquiring new secret.
1251 // If we wanted to be fascist, we could now do
1252 // CssmError::throwMe(CSSM_ERRCODE_SAMPLE_VALUE_NOT_SUPPORTED);
1253 // But instead we try to be tolerant and continue on.
1254 // This DOES however count as an explicit attempt at specifying unlock,
1255 // so we will no longer try the default case below...
1256 secinfo("KCdb", "%p unknown sub-sample acquisition (%d) ignored",
1257 this, sample.type());
1258 break;
1259 }
1260 }
1261 } else {
1262 // default action -- interactive (only)
1263 QueryNewPassphrase query(*this, reason);
1264 StSyncLock<Mutex, Mutex> uisync(common().uiLock(), common());
1265 query.inferHints(Server::process());
1266 CssmAutoData passphrase(Allocator::standard(Allocator::sensitive));
1267 CssmAutoData oldPassphrase(Allocator::standard(Allocator::sensitive));
1268 if (query(oldPassphrase, passphrase) == SecurityAgent::noReason) {
1269 common().setup(NULL, passphrase);
1270 change_secret_on_keybag(*this, oldPassphrase.data(), (int)oldPassphrase.length(), passphrase.data(), (int)passphrase.length());
1271 return true;
1272 }
1273 }
1274
1275 // out of options - no secret obtained
1276 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
1277 }
1278
1279
1280 //
1281 // Given a (truncated) Database credentials TypedList specifying a master key,
1282 // locate the key and return a reference to it.
1283 //
1284 CssmClient::Key KeychainDatabase::keyFromCreds(const TypedList &sample, unsigned int requiredLength)
1285 {
1286 // decode TypedList structure (sample type; Data:CSPHandle; Data:CSSM_KEY)
1287 assert(sample.type() == CSSM_SAMPLE_TYPE_SYMMETRIC_KEY || sample.type() == CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY);
1288 if (sample.length() != requiredLength
1289 || sample[1].type() != CSSM_LIST_ELEMENT_DATUM
1290 || sample[2].type() != CSSM_LIST_ELEMENT_DATUM
1291 || (requiredLength == 4 && sample[3].type() != CSSM_LIST_ELEMENT_DATUM))
1292 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
1293 KeyHandle &handle = *sample[1].data().interpretedAs<KeyHandle>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
1294 // We used to be able to check the length but supporting multiple client
1295 // architectures dishes that (sizeof(CSSM_KEY) varies due to alignment and
1296 // field-size differences). The decoding in the transition layer should
1297 // serve as a sufficient garbling check anyway.
1298 if (sample[2].data().data() == NULL)
1299 CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
1300 CssmKey &key = *sample[2].data().interpretedAs<CssmKey>();
1301
1302 if (key.header().cspGuid() == gGuidAppleCSPDL) {
1303 // handleOrKey is a SecurityServer KeyHandle; ignore key argument
1304 return safer_cast<LocalKey &>(*Server::key(handle));
1305 } else
1306 if (sample.type() == CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY) {
1307 /*
1308 Contents (see DefaultCredentials::unlockKey in libsecurity_keychain/defaultcreds.cpp)
1309
1310 sample[0] sample type
1311 sample[1] csp handle for master or wrapping key; is really a keyhandle
1312 sample[2] masterKey [not used since securityd cannot interpret; use sample[1] handle instead]
1313 sample[3] UnlockReferralRecord data, in this case the flattened symmetric key
1314 */
1315
1316 // RefPointer<Key> Server::key(KeyHandle key)
1317 KeyHandle keyhandle = *sample[1].data().interpretedAs<KeyHandle>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
1318 CssmData &flattenedKey = sample[3].data();
1319 RefPointer<Key> unwrappingKey = Server::key(keyhandle);
1320 Database &db=unwrappingKey->database();
1321
1322 CssmKey rawWrappedKey;
1323 unflattenKey(flattenedKey, rawWrappedKey);
1324
1325 RefPointer<Key> masterKey;
1326 CssmData emptyDescriptiveData;
1327 const AccessCredentials *cred = NULL;
1328 const AclEntryPrototype *owner = NULL;
1329 CSSM_KEYUSE usage = CSSM_KEYUSE_ANY;
1330 CSSM_KEYATTR_FLAGS attrs = CSSM_KEYATTR_EXTRACTABLE; //CSSM_KEYATTR_RETURN_REF |
1331
1332 // Get default credentials for unwrappingKey (the one on the token)
1333 // Copied from Statics::Statics() in libsecurity_keychain/aclclient.cpp
1334 // Following KeyItem::getCredentials, one sees that the "operation" parameter
1335 // e.g. "CSSM_ACL_AUTHORIZATION_DECRYPT" is ignored
1336 Allocator &alloc = Allocator::standard();
1337 AutoCredentials promptCred(alloc, 3);// enable interactive prompting
1338
1339 // promptCred: a credential permitting user prompt confirmations
1340 // contains:
1341 // a KEYCHAIN_PROMPT sample, both by itself and in a THRESHOLD
1342 // a PROMPTED_PASSWORD sample
1343 promptCred.sample(0) = TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT);
1344 promptCred.sample(1) = TypedList(alloc, CSSM_SAMPLE_TYPE_THRESHOLD,
1345 new(alloc) ListElement(TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT)));
1346 promptCred.sample(2) = TypedList(alloc, CSSM_SAMPLE_TYPE_PROMPTED_PASSWORD,
1347 new(alloc) ListElement(alloc, CssmData()));
1348
1349 // This unwrap object is here just to provide a context
1350 CssmClient::UnwrapKey unwrap(Server::csp(), CSSM_ALGID_NONE); //ok to lie about csp here
1351 unwrap.mode(CSSM_ALGMODE_NONE);
1352 unwrap.padding(CSSM_PADDING_PKCS1);
1353 unwrap.cred(promptCred);
1354 unwrap.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT, uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7));
1355 Security::Context *tmpContext;
1356 CSSM_CC_HANDLE CCHandle = unwrap.handle();
1357 /*CSSM_RETURN rx = */ CSSM_GetContext (CCHandle, (CSSM_CONTEXT_PTR *)&tmpContext);
1358
1359 // OK, this is skanky but necessary. We overwrite fields in the context struct
1360
1361 tmpContext->ContextType = CSSM_ALGCLASS_ASYMMETRIC;
1362 tmpContext->AlgorithmType = CSSM_ALGID_RSA;
1363
1364 db.unwrapKey(*tmpContext, cred, owner, unwrappingKey, NULL, usage, attrs,
1365 rawWrappedKey, masterKey, emptyDescriptiveData);
1366
1367 Allocator::standard().free(rawWrappedKey.KeyData.Data);
1368
1369 return safer_cast<LocalKey &>(*masterKey).key();
1370 }
1371 else if (sample.type() == CSSM_SAMPLE_TYPE_SYMMETRIC_KEY && sample.length() == 4 && sample[3].data().length() > 0) {
1372 /*
1373 Contents (see MasterKeyUnlockCredentials in libsecurity_cdsa_client/lib/aclclient.cpp)
1374
1375 sample[0] sample type
1376 sample[1] 0, since we don't have a valid handle
1377 sample[2] CssmKey of the masterKey [can't immediately use since it includes a CSSM_DATA struct with pointers]
1378 sample[3] flattened symmetric master key, including the key data
1379 */
1380
1381 // Fix up key to include actual data
1382 CssmData &flattenedKey = sample[3].data();
1383 unflattenKey(flattenedKey, key);
1384
1385 // Check that we have a reasonable key
1386 if (key.header().blobType() != CSSM_KEYBLOB_RAW) {
1387 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE);
1388 }
1389 if (key.header().keyClass() != CSSM_KEYCLASS_SESSION_KEY) {
1390 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
1391 }
1392
1393 // bring the key into the CSP and return it
1394 return CssmClient::Key(Server::csp(), key, true);
1395 } else {
1396 // not a KeyHandle reference; use key as a raw key
1397 if (key.header().blobType() != CSSM_KEYBLOB_RAW)
1398 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE);
1399 if (key.header().keyClass() != CSSM_KEYCLASS_SESSION_KEY)
1400 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
1401 return CssmClient::Key(Server::csp(), key, true);
1402 }
1403 }
1404
1405 void unflattenKey(const CssmData &flatKey, CssmKey &rawKey)
1406 {
1407 // The format we're expecting is a CSSM_KEY followed by the actual key data:
1408 // CSSM_KEY : KEY DATA
1409 // which is approximately:
1410 // h2ni(CSSM_KEYHEADER) : 4 bytes padding : CSSM_DATA{?:?} : KEY BYTES
1411 //
1412 // Note that CSSM_KEY includes a CSSM_DATA struct, which we will ignore as it has pointers.
1413 // The pointer and length will be set to whatever key data follows the CSSM_KEY in rawKey.
1414
1415 // unflatten the raw input key naively: key header then key data
1416 // We also convert it back to host byte order
1417 // A CSSM_KEY is a CSSM_KEYHEADER followed by a CSSM_DATA
1418
1419 // Now copy: header, then key struct, then key data
1420 memcpy(&rawKey.KeyHeader, flatKey.Data, sizeof(CSSM_KEYHEADER));
1421 memcpy(&rawKey.KeyData, flatKey.Data + sizeof(CSSM_KEYHEADER), sizeof(CSSM_DATA));
1422 size_t keyDataLength = flatKey.length() - sizeof(CSSM_KEY);
1423 rawKey.KeyData.Data = Allocator::standard().malloc<uint8>(keyDataLength);
1424 rawKey.KeyData.Length = keyDataLength;
1425 memcpy(rawKey.KeyData.Data, flatKey.Data + sizeof(CSSM_KEY), keyDataLength);
1426 Security::n2hi(rawKey.KeyHeader); // convert it to host byte order
1427 }
1428
1429
1430 //
1431 // Verify a putative database passphrase.
1432 // If the database is already unlocked, just check the passphrase.
1433 // Otherwise, unlock with that passphrase and report success.
1434 // Caller must hold the common lock.
1435 //
1436 bool KeychainDatabase::validatePassphrase(const CssmData &passphrase) const
1437 {
1438 if (common().hasMaster()) {
1439 // verify against known secret
1440 return common().validatePassphrase(passphrase);
1441 } else {
1442 // no master secret - perform "blind" unlock to avoid actual unlock
1443 try {
1444 DatabaseCryptoCore test;
1445 test.setup(mBlob, passphrase);
1446 test.decodeCore(mBlob, NULL);
1447 return true;
1448 } catch (...) {
1449 return false;
1450 }
1451 }
1452 }
1453
1454
1455 //
1456 // Lock this database
1457 //
1458 void KeychainDatabase::lockDb()
1459 {
1460 common().lockDb();
1461 }
1462
1463
1464 //
1465 // Given a Key for this database, encode it into a blob and return it.
1466 //
1467 KeyBlob *KeychainDatabase::encodeKey(const CssmKey &key, const CssmData &pubAcl, const CssmData &privAcl)
1468 {
1469 bool inTheClear = false;
1470 if((key.keyClass() == CSSM_KEYCLASS_PUBLIC_KEY) &&
1471 !(key.attribute(CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT))) {
1472 inTheClear = true;
1473 }
1474 StLock<Mutex> _(common());
1475 if(!inTheClear)
1476 makeUnlocked(false);
1477
1478 // tell the cryptocore to form the key blob
1479 return common().encodeKeyCore(key, pubAcl, privAcl, inTheClear);
1480 }
1481
1482
1483 //
1484 // Given a "blobbed" key for this database, decode it into its real
1485 // key object and (re)populate its ACL.
1486 //
1487 void KeychainDatabase::decodeKey(KeyBlob *blob, CssmKey &key, void * &pubAcl, void * &privAcl)
1488 {
1489 StLock<Mutex> _(common());
1490
1491 if(!blob->isClearText())
1492 makeUnlocked(false); // we need our keys
1493
1494 common().decodeKeyCore(blob, key, pubAcl, privAcl);
1495 // memory protocol: pubAcl points into blob; privAcl was allocated
1496
1497 activity();
1498 }
1499
1500 //
1501 // Given a KeychainKey (that implicitly belongs to another keychain),
1502 // return it encoded using this keychain's operational secrets.
1503 //
1504 KeyBlob *KeychainDatabase::recodeKey(KeychainKey &oldKey)
1505 {
1506 if (mRecodingSource != &oldKey.referent<KeychainDatabase>()) {
1507 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
1508 }
1509
1510 // To protect this operation, we need to take the mutex for both our common and the remote key's common in some defined order.
1511 // Grab the common being cloned (oldKey's) first, and then the common receiving the recoding (ours).
1512 StLock<Mutex> _ (oldKey.referent<KeychainDatabase>().common());
1513 StLock<Mutex> __(common());
1514
1515 oldKey.instantiateAcl(); // make sure key is decoded
1516 CssmData publicAcl, privateAcl;
1517 oldKey.exportBlob(publicAcl, privateAcl);
1518 // NB: blob's memory belongs to caller, not the common
1519
1520 /*
1521 * Make sure the new key is in the same cleartext/encrypted state.
1522 */
1523 bool inTheClear = false;
1524 assert(oldKey.blob());
1525 if(oldKey.blob() && oldKey.blob()->isClearText()) {
1526 /* careful....*/
1527 inTheClear = true;
1528 }
1529 KeyBlob *blob = common().encodeKeyCore(oldKey.cssmKey(), publicAcl, privateAcl, inTheClear);
1530 oldKey.acl().allocator.free(publicAcl);
1531 oldKey.acl().allocator.free(privateAcl);
1532 return blob;
1533 }
1534
1535
1536 //
1537 // Modify database parameters
1538 //
1539 void KeychainDatabase::setParameters(const DBParameters &params)
1540 {
1541 StLock<Mutex> _(common());
1542 makeUnlocked(false);
1543 common().mParams = params;
1544 common().invalidateBlob(); // invalidate old blobs
1545 activity(); // (also resets the timeout timer)
1546 secinfo("KCdb", "%p common %p(%s) set params=(%u,%u)",
1547 this, &common(), dbName(), params.idleTimeout, params.lockOnSleep);
1548 }
1549
1550
1551 //
1552 // Retrieve database parameters
1553 //
1554 void KeychainDatabase::getParameters(DBParameters &params)
1555 {
1556 StLock<Mutex> _(common());
1557 makeUnlocked(false);
1558 params = common().mParams;
1559 //activity(); // getting parameters does not reset the idle timer
1560 }
1561
1562
1563 //
1564 // RIGHT NOW, database ACLs are attached to the database.
1565 // This will soon move upstairs.
1566 //
1567 SecurityServerAcl &KeychainDatabase::acl()
1568 {
1569 return *this;
1570 }
1571
1572
1573 //
1574 // Intercept ACL change requests and reset blob validity
1575 //
1576 void KeychainDatabase::instantiateAcl()
1577 {
1578 StLock<Mutex> _(common());
1579 makeUnlocked(false);
1580 }
1581
1582 void KeychainDatabase::changedAcl()
1583 {
1584 StLock<Mutex> _(common());
1585 version = 0;
1586 }
1587
1588
1589 //
1590 // Check an incoming DbBlob for basic viability
1591 //
1592 void KeychainDatabase::validateBlob(const DbBlob *blob)
1593 {
1594 // perform basic validation on the blob
1595 assert(blob);
1596 blob->validate(CSSMERR_APPLEDL_INVALID_DATABASE_BLOB);
1597 switch (blob->version()) {
1598 #if defined(COMPAT_OSX_10_0)
1599 case DbBlob::version_MacOS_10_0:
1600 break;
1601 #endif
1602 case DbBlob::version_MacOS_10_1:
1603 break;
1604 case DbBlob::version_partition:
1605 break;
1606 default:
1607 CssmError::throwMe(CSSMERR_APPLEDL_INCOMPATIBLE_DATABASE_BLOB);
1608 }
1609 }
1610
1611 //
1612 // Check if this database is currently recoding
1613 //
1614 bool KeychainDatabase::isRecoding()
1615 {
1616 secnotice("integrity", "recoding source: %p", mRecodingSource.get());
1617 return (mRecodingSource.get() != NULL || mRecoded);
1618 }
1619
1620 //
1621 // Mark ourselves as no longer recoding
1622 //
1623 void KeychainDatabase::recodeFinished()
1624 {
1625 secnotice("integrity", "recoding finished");
1626 mRecodingSource = NULL;
1627 mRecoded = false;
1628 }
1629
1630
1631 //
1632 // Debugging support
1633 //
1634 #if defined(DEBUGDUMP)
1635
1636 void KeychainDbCommon::dumpNode()
1637 {
1638 PerSession::dumpNode();
1639 uint32 sig; memcpy(&sig, &mIdentifier.signature(), sizeof(sig));
1640 Debug::dump(" %s[%8.8x]", mIdentifier.dbName(), sig);
1641 if (isLocked()) {
1642 Debug::dump(" locked");
1643 } else {
1644 time_t whenTime = time_t(when());
1645 Debug::dump(" unlocked(%24.24s/%.2g)", ctime(&whenTime),
1646 (when() - Time::now()).seconds());
1647 }
1648 Debug::dump(" params=(%u,%u)", mParams.idleTimeout, mParams.lockOnSleep);
1649 }
1650
1651 void KeychainDatabase::dumpNode()
1652 {
1653 PerProcess::dumpNode();
1654 Debug::dump(" %s vers=%u",
1655 mValidData ? " data" : " nodata", version);
1656 if (mBlob) {
1657 uint32 sig; memcpy(&sig, &mBlob->randomSignature, sizeof(sig));
1658 Debug::dump(" blob=%p[%8.8x]", mBlob, sig);
1659 } else {
1660 Debug::dump(" noblob");
1661 }
1662 }
1663
1664 #endif //DEBUGDUMP
1665
1666
1667 //
1668 // DbCommon basic features
1669 //
1670 KeychainDbCommon::KeychainDbCommon(Session &ssn, const DbIdentifier &id, uint32 requestedVersion)
1671 : LocalDbCommon(ssn), DatabaseCryptoCore(requestedVersion), sequence(0), version(1), mIdentifier(id),
1672 mIsLocked(true), mValidParams(false), mLoginKeychain(false)
1673 {
1674 // match existing DbGlobal or create a new one
1675 {
1676 Server &server = Server::active();
1677 StLock<Mutex> _(server);
1678 if (KeychainDbGlobal *dbglobal =
1679 server.findFirst<KeychainDbGlobal, const DbIdentifier &>(&KeychainDbGlobal::identifier, identifier())) {
1680 parent(*dbglobal);
1681 secinfo("KCdb", "%p linking to existing DbGlobal %p", this, dbglobal);
1682 } else {
1683 // DbGlobal not present; make a new one
1684 parent(*new KeychainDbGlobal(identifier()));
1685 secinfo("KCdb", "%p linking to new DbGlobal %p", this, &global());
1686 }
1687
1688 // link lifetime to the Session
1689 session().addReference(*this);
1690
1691 if (strcasestr(id.dbName(), "login.keychain") != NULL) {
1692 mLoginKeychain = true;
1693 }
1694 }
1695 }
1696
1697 void KeychainDbCommon::initializeKeybag() {
1698 if (mLoginKeychain && !session().keybagGetState(session_keybag_loaded)) {
1699 service_context_t context = session().get_current_service_context();
1700 if (service_client_kb_load(&context) == 0) {
1701 session().keybagSetState(session_keybag_loaded);
1702 }
1703 }
1704 }
1705
1706 KeychainDbCommon::KeychainDbCommon(Session &ssn, const DbIdentifier &id, KeychainDbCommon& toClone)
1707 : LocalDbCommon(ssn), DatabaseCryptoCore(toClone.mBlobVersion), sequence(toClone.sequence), mParams(toClone.mParams), version(toClone.version),
1708 mIdentifier(id), mIsLocked(toClone.mIsLocked), mValidParams(toClone.mValidParams), mLoginKeychain(toClone.mLoginKeychain)
1709 {
1710 cloneFrom(toClone);
1711
1712 {
1713 Server &server = Server::active();
1714 StLock<Mutex> _(server);
1715 if (KeychainDbGlobal *dbglobal =
1716 server.findFirst<KeychainDbGlobal, const DbIdentifier &>(&KeychainDbGlobal::identifier, identifier())) {
1717 parent(*dbglobal);
1718 secinfo("KCdb", "%p linking to existing DbGlobal %p", this, dbglobal);
1719 } else {
1720 // DbGlobal not present; make a new one
1721 parent(*new KeychainDbGlobal(identifier()));
1722 secinfo("KCdb", "%p linking to new DbGlobal %p", this, &global());
1723 }
1724 session().addReference(*this);
1725 }
1726 }
1727
1728 KeychainDbCommon::~KeychainDbCommon()
1729 {
1730 secinfo("KCdb", "releasing keychain %p %s", this, (char*)this->dbName());
1731
1732 // explicitly unschedule ourselves
1733 Server::active().clearTimer(this);
1734 if (mLoginKeychain) {
1735 session().keybagClearState(session_keybag_unlocked);
1736 }
1737 // remove ourselves from mCommonSet
1738 kill();
1739 }
1740
1741 void KeychainDbCommon::cloneFrom(KeychainDbCommon& toClone, uint32 requestedVersion) {
1742 // don't clone the mIdentifier
1743 sequence = toClone.sequence;
1744 mParams = toClone.mParams;
1745 version = toClone.version;
1746 mIsLocked = toClone.mIsLocked;
1747 mValidParams = toClone.mValidParams;
1748 mLoginKeychain = toClone.mLoginKeychain;
1749
1750 DatabaseCryptoCore::initializeFrom(toClone, requestedVersion);
1751 }
1752
1753 void KeychainDbCommon::kill()
1754 {
1755 StReadWriteLock _(mRWCommonLock, StReadWriteLock::Write);
1756 mCommonSet.erase(this);
1757 }
1758
1759 KeychainDbGlobal &KeychainDbCommon::global() const
1760 {
1761 return parent<KeychainDbGlobal>();
1762 }
1763
1764
1765 void KeychainDbCommon::select()
1766 { this->ref(); }
1767
1768 void KeychainDbCommon::unselect()
1769 { this->unref(); }
1770
1771
1772
1773 void KeychainDbCommon::makeNewSecrets()
1774 {
1775 // we already have a master key (right?)
1776 assert(hasMaster());
1777
1778 // tell crypto core to generate the use keys
1779 DatabaseCryptoCore::generateNewSecrets();
1780
1781 // we're now officially "unlocked"; set the timer
1782 setUnlocked();
1783 }
1784
1785
1786 //
1787 // All unlocking activity ultimately funnels through this method.
1788 // This unlocks a DbCommon using the secrets setup in its crypto core
1789 // component, and performs all the housekeeping needed to represent
1790 // the state change.
1791 // Returns true if unlock was successful, false if it failed due to
1792 // invalid/insufficient secrets. Throws on other errors.
1793 //
1794 bool KeychainDbCommon::unlockDb(DbBlob *blob, void **privateAclBlob)
1795 {
1796 try {
1797 // Tell the cryptocore to (try to) decode itself. This will fail
1798 // in an astonishing variety of ways if the passphrase is wrong.
1799 assert(hasMaster());
1800 decodeCore(blob, privateAclBlob);
1801 secinfo("KCdb", "%p unlock successful", this);
1802 } catch (...) {
1803 secinfo("KCdb", "%p unlock failed", this);
1804 return false;
1805 }
1806
1807 // get the database parameters only if we haven't got them yet
1808 if (!mValidParams) {
1809 mParams = blob->params;
1810 n2hi(mParams.idleTimeout);
1811 mValidParams = true; // sticky
1812 }
1813
1814 bool isLocked = mIsLocked;
1815
1816 setUnlocked(); // mark unlocked
1817
1818 if (isLocked) {
1819 // broadcast unlock notification, but only if we were previously locked
1820 notify(kNotificationEventUnlocked);
1821 secinfo("KCdb", "unlocking keychain %p %s", this, (char*)this->dbName());
1822 }
1823 return true;
1824 }
1825
1826 void KeychainDbCommon::setUnlocked()
1827 {
1828 session().addReference(*this); // active/held
1829 mIsLocked = false; // mark unlocked
1830 activity(); // set timeout timer
1831 }
1832
1833
1834 void KeychainDbCommon::lockDb()
1835 {
1836 bool lock = false;
1837 {
1838 StLock<Mutex> _(*this);
1839 if (!isLocked()) {
1840 DatabaseCryptoCore::invalidate();
1841 notify(kNotificationEventLocked);
1842 secinfo("KCdb", "locking keychain %p %s", this, (char*)this->dbName());
1843 Server::active().clearTimer(this);
1844
1845 mIsLocked = true; // mark locked
1846 lock = true;
1847
1848 // this call may destroy us if we have no databases anymore
1849 session().removeReference(*this);
1850 }
1851 }
1852 }
1853
1854
1855 DbBlob *KeychainDbCommon::encode(KeychainDatabase &db)
1856 {
1857 assert(!isLocked()); // must have been unlocked by caller
1858
1859 // export database ACL to blob form
1860 CssmData pubAcl, privAcl;
1861 db.acl().exportBlob(pubAcl, privAcl);
1862
1863 // tell the cryptocore to form the blob
1864 DbBlob form;
1865 form.randomSignature = identifier();
1866 form.sequence = sequence;
1867 form.params = mParams;
1868 h2ni(form.params.idleTimeout);
1869
1870 assert(hasMaster());
1871 DbBlob *blob = encodeCore(form, pubAcl, privAcl);
1872
1873 // clean up and go
1874 db.acl().allocator.free(pubAcl);
1875 db.acl().allocator.free(privAcl);
1876 return blob;
1877 }
1878
1879
1880 //
1881 // Perform deferred lock processing for a database.
1882 //
1883 void KeychainDbCommon::action()
1884 {
1885 secinfo("KCdb", "common %s(%p) locked by timer", dbName(), this);
1886 lockDb();
1887 }
1888
1889 void KeychainDbCommon::activity()
1890 {
1891 if (!isLocked()) {
1892 secinfo("KCdb", "setting DbCommon %p timer to %d",
1893 this, int(mParams.idleTimeout));
1894 Server::active().setTimer(this, Time::Interval(int(mParams.idleTimeout)));
1895 }
1896 }
1897
1898 void KeychainDbCommon::sleepProcessing()
1899 {
1900 secinfo("KCdb", "common %s(%p) sleep-lock processing", dbName(), this);
1901 if (mParams.lockOnSleep && !isDefaultSystemKeychain()) {
1902 StLock<Mutex> _(*this);
1903 lockDb();
1904 }
1905 }
1906
1907 void KeychainDbCommon::lockProcessing()
1908 {
1909 lockDb();
1910 }
1911
1912
1913 //
1914 // We consider a keychain to belong to the system domain if it resides
1915 // in /Library/Keychains. That's not exactly fool-proof, but we don't
1916 // currently have any internal markers to interrogate.
1917 //
1918 bool KeychainDbCommon::belongsToSystem() const
1919 {
1920 if (const char *name = this->dbName())
1921 return !strncmp(name, "/Library/Keychains/", 19);
1922 return false;
1923 }
1924
1925 bool KeychainDbCommon::isDefaultSystemKeychain() const
1926 {
1927 // /Library/Keychains/System.keychain (34)
1928 if (const char *name = this->dbName())
1929 return !strncmp(name, "/Library/Keychains/System.keychain", 34);
1930 return false;
1931 }
1932
1933 //
1934 // Keychain global objects
1935 //
1936 KeychainDbGlobal::KeychainDbGlobal(const DbIdentifier &id)
1937 : mIdentifier(id)
1938 {
1939 }
1940
1941 KeychainDbGlobal::~KeychainDbGlobal()
1942 {
1943 secinfo("KCdb", "DbGlobal %p destroyed", this);
1944 }