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