1/* 2 * Copyright (c) 2000-2004,2011-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// Keychains.cpp 27// 28 29#include "KCEventNotifier.h" 30#include "Keychains.h" 31 32#include "Item.h" 33#include "KCCursor.h" 34#include "Globals.h" 35#include <security_cdsa_utilities/Schema.h> 36#include <security_cdsa_client/keychainacl.h> 37#include <security_cdsa_utilities/cssmacl.h> 38#include <security_cdsa_utilities/cssmdb.h> 39#include <security_utilities/trackingallocator.h> 40#include <security_keychain/SecCFTypes.h> 41 42#include "SecKeychainPriv.h" 43 44#include <Security/SecKeychainItemPriv.h> 45#include <CoreFoundation/CoreFoundation.h> 46#include "DLDbListCFPref.h" 47#include <fcntl.h> 48#include <sys/param.h> 49#include <syslog.h> 50#include <sys/stat.h> 51#include <sys/socket.h> 52#include <sys/un.h> 53#include <sys/types.h> 54#include <sys/time.h> 55 56static dispatch_once_t SecKeychainSystemKeychainChecked; 57 58OSStatus SecKeychainSystemKeychainCheckWouldDeadlock() 59{ 60 dispatch_once(&SecKeychainSystemKeychainChecked, ^{}); 61 return errSecSuccess; 62} 63 64using namespace KeychainCore; 65using namespace CssmClient; 66 67 68typedef struct EventItem 69{ 70 SecKeychainEvent kcEvent; 71 Item item; 72} EventItem; 73 74typedef std::list<EventItem> EventBufferSuper; 75class EventBuffer : public EventBufferSuper 76{ 77public: 78 EventBuffer () {} 79 virtual ~EventBuffer (); 80}; 81 82 83EventBuffer::~EventBuffer () 84{ 85} 86 87 88 89// 90// KeychainSchemaImpl 91// 92KeychainSchemaImpl::KeychainSchemaImpl(const Db &db) : mMutex(Mutex::recursive) 93{ 94 DbCursor relations(db); 95 relations->recordType(CSSM_DL_DB_SCHEMA_INFO); 96 DbAttributes relationRecord(db, 1); 97 relationRecord.add(Schema::RelationID); 98 DbUniqueRecord outerUniqueId(db); 99 100 while (relations->next(&relationRecord, NULL, outerUniqueId)) 101 { 102 DbUniqueRecord uniqueId(db); 103 104 uint32 relationID = relationRecord.at(0); 105 if (CSSM_DB_RECORDTYPE_SCHEMA_START <= relationID 106 && relationID < CSSM_DB_RECORDTYPE_SCHEMA_END) 107 continue; 108 109 // Create a cursor on the SCHEMA_ATTRIBUTES table for records with 110 // RelationID == relationID 111 DbCursor attributes(db); 112 attributes->recordType(CSSM_DL_DB_SCHEMA_ATTRIBUTES); 113 attributes->add(CSSM_DB_EQUAL, Schema::RelationID, relationID); 114 115 // Set up a record for retriving the SCHEMA_ATTRIBUTES 116 DbAttributes attributeRecord(db, 2); 117 attributeRecord.add(Schema::AttributeFormat); 118 attributeRecord.add(Schema::AttributeID); 119 120 RelationInfoMap &rim = mDatabaseInfoMap[relationID]; 121 while (attributes->next(&attributeRecord, NULL, uniqueId)) 122 rim[attributeRecord.at(1)] = attributeRecord.at(0); 123 124 // Create a cursor on the CSSM_DL_DB_SCHEMA_INDEXES table for records 125 // with RelationID == relationID 126 DbCursor indexes(db); 127 indexes->recordType(CSSM_DL_DB_SCHEMA_INDEXES); 128 indexes->conjunctive(CSSM_DB_AND); 129 indexes->add(CSSM_DB_EQUAL, Schema::RelationID, relationID); 130 indexes->add(CSSM_DB_EQUAL, Schema::IndexType, 131 uint32(CSSM_DB_INDEX_UNIQUE)); 132 133 // Set up a record for retriving the SCHEMA_INDEXES 134 DbAttributes indexRecord(db, 1); 135 indexRecord.add(Schema::AttributeID); 136 137 CssmAutoDbRecordAttributeInfo &infos = 138 *new CssmAutoDbRecordAttributeInfo(); 139 mPrimaryKeyInfoMap. 140 insert(PrimaryKeyInfoMap::value_type(relationID, &infos)); 141 infos.DataRecordType = relationID; 142 while (indexes->next(&indexRecord, NULL, uniqueId)) 143 { 144 CssmDbAttributeInfo &info = infos.add(); 145 info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER; 146 info.Label.AttributeID = indexRecord.at(0); 147 // @@@ Might insert bogus value if DB is corrupt 148 info.AttributeFormat = rim[info.Label.AttributeID]; 149 } 150 } 151} 152 153KeychainSchemaImpl::~KeychainSchemaImpl() 154{ 155 try 156 { 157 map<CSSM_DB_RECORDTYPE, CssmAutoDbRecordAttributeInfo *>::iterator it = mPrimaryKeyInfoMap.begin(); 158 while (it != mPrimaryKeyInfoMap.end()) 159 { 160 delete it->second; 161 it++; 162 } 163 // for_each_map_delete(mPrimaryKeyInfoMap.begin(), mPrimaryKeyInfoMap.end()); 164 } 165 catch(...) 166 { 167 } 168} 169 170const KeychainSchemaImpl::RelationInfoMap & 171KeychainSchemaImpl::relationInfoMapFor(CSSM_DB_RECORDTYPE recordType) const 172{ 173 DatabaseInfoMap::const_iterator dit = mDatabaseInfoMap.find(recordType); 174 if (dit == mDatabaseInfoMap.end()) 175 MacOSError::throwMe(errSecNoSuchClass); 176 return dit->second; 177} 178 179bool KeychainSchemaImpl::hasRecordType (CSSM_DB_RECORDTYPE recordType) const 180{ 181 DatabaseInfoMap::const_iterator it = mDatabaseInfoMap.find(recordType); 182 return it != mDatabaseInfoMap.end(); 183} 184 185bool 186KeychainSchemaImpl::hasAttribute(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const 187{ 188 try 189 { 190 const RelationInfoMap &rmap = relationInfoMapFor(recordType); 191 RelationInfoMap::const_iterator rit = rmap.find(attributeId); 192 return rit != rmap.end(); 193 } 194 catch (MacOSError result) 195 { 196 if (result.osStatus () == errSecNoSuchClass) 197 { 198 return false; 199 } 200 else 201 { 202 throw; 203 } 204 } 205} 206 207CSSM_DB_ATTRIBUTE_FORMAT 208KeychainSchemaImpl::attributeFormatFor(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const 209{ 210 const RelationInfoMap &rmap = relationInfoMapFor(recordType); 211 RelationInfoMap::const_iterator rit = rmap.find(attributeId); 212 if (rit == rmap.end()) 213 MacOSError::throwMe(errSecNoSuchAttr); 214 215 return rit->second; 216} 217 218CssmDbAttributeInfo 219KeychainSchemaImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const 220{ 221 CSSM_DB_ATTRIBUTE_INFO info; 222 info.AttributeFormat = attributeFormatFor(recordType, attributeId); 223 info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER; 224 info.Label.AttributeID = attributeId; 225 226 return info; 227} 228 229void 230KeychainSchemaImpl::getAttributeInfoForRecordType(CSSM_DB_RECORDTYPE recordType, SecKeychainAttributeInfo **Info) const 231{ 232 const RelationInfoMap &rmap = relationInfoMapFor(recordType); 233 234 SecKeychainAttributeInfo *theList=reinterpret_cast<SecKeychainAttributeInfo *>(malloc(sizeof(SecKeychainAttributeInfo))); 235 236 size_t capacity=rmap.size(); 237 UInt32 *tagBuf=reinterpret_cast<UInt32 *>(malloc(capacity*sizeof(UInt32))); 238 UInt32 *formatBuf=reinterpret_cast<UInt32 *>(malloc(capacity*sizeof(UInt32))); 239 UInt32 i=0; 240 241 242 for (RelationInfoMap::const_iterator rit = rmap.begin(); rit != rmap.end(); ++rit) 243 { 244 if (i>=capacity) 245 { 246 capacity *= 2; 247 if (capacity <= i) capacity = i + 1; 248 tagBuf=reinterpret_cast<UInt32 *>(realloc(tagBuf, (capacity*sizeof(UInt32)))); 249 formatBuf=reinterpret_cast<UInt32 *>(realloc(tagBuf, (capacity*sizeof(UInt32)))); 250 } 251 tagBuf[i]=rit->first; 252 formatBuf[i++]=rit->second; 253 } 254 255 theList->count=i; 256 theList->tag=tagBuf; 257 theList->format=formatBuf; 258 *Info=theList; 259} 260 261 262const CssmAutoDbRecordAttributeInfo & 263KeychainSchemaImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType) const 264{ 265 PrimaryKeyInfoMap::const_iterator it; 266 it = mPrimaryKeyInfoMap.find(recordType); 267 268 if (it == mPrimaryKeyInfoMap.end()) 269 MacOSError::throwMe(errSecNoSuchClass); // @@@ Not really but whatever. 270 271 return *it->second; 272} 273 274bool 275KeychainSchemaImpl::operator <(const KeychainSchemaImpl &other) const 276{ 277 return mDatabaseInfoMap < other.mDatabaseInfoMap; 278} 279 280bool 281KeychainSchemaImpl::operator ==(const KeychainSchemaImpl &other) const 282{ 283 return mDatabaseInfoMap == other.mDatabaseInfoMap; 284} 285 286void 287KeychainSchemaImpl::didCreateRelation(CSSM_DB_RECORDTYPE relationID, 288 const char *inRelationName, 289 uint32 inNumberOfAttributes, 290 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *pAttributeInfo, 291 uint32 inNumberOfIndexes, 292 const CSSM_DB_SCHEMA_INDEX_INFO *pIndexInfo) 293{ 294 StLock<Mutex>_(mMutex); 295 296 if (CSSM_DB_RECORDTYPE_SCHEMA_START <= relationID 297 && relationID < CSSM_DB_RECORDTYPE_SCHEMA_END) 298 return; 299 300 // if our schema is already in the map, return 301 if (mPrimaryKeyInfoMap.find(relationID) != mPrimaryKeyInfoMap.end()) 302 { 303 return; 304 } 305 306 RelationInfoMap &rim = mDatabaseInfoMap[relationID]; 307 for (uint32 ix = 0; ix < inNumberOfAttributes; ++ix) 308 rim[pAttributeInfo[ix].AttributeId] = pAttributeInfo[ix].DataType; 309 310 CssmAutoDbRecordAttributeInfo *infos = new CssmAutoDbRecordAttributeInfo(); 311 312 mPrimaryKeyInfoMap. 313 insert(PrimaryKeyInfoMap::value_type(relationID, infos)); 314 infos->DataRecordType = relationID; 315 for (uint32 ix = 0; ix < inNumberOfIndexes; ++ix) 316 if (pIndexInfo[ix].IndexType == CSSM_DB_INDEX_UNIQUE) 317 { 318 CssmDbAttributeInfo &info = infos->add(); 319 info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER; 320 info.Label.AttributeID = pIndexInfo[ix].AttributeId; 321 info.AttributeFormat = rim[info.Label.AttributeID]; 322 } 323} 324 325 326 327KeychainSchema::~KeychainSchema() 328 329{ 330} 331 332 333 334struct Event 335{ 336 SecKeychainEvent eventCode; 337 PrimaryKey primaryKey; 338}; 339typedef std::list<Event> EventList; 340 341#define SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME "/var/run/systemkeychaincheck" 342#define SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".socket") 343#define SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".done") 344 345static void check_system_keychain() 346{ 347 // sadly we can't use XPC here, XPC_DOMAIN_TYPE_SYSTEM doesn't exist yet. Also xpc-helper uses the 348 // keychain API (I assume for checking codesign things). So we use Unix Domain Sockets. 349 350 // NOTE: if we hit a system error we attempt to log it, and then just don't check the system keychain. 351 // In theory a system might be able to recover from this state if we let it try to muddle along, and 352 // past behaviour didn't even try this hard to do the keychain check. In particular we could be in a 353 // sandbox'ed process. So we just do our best and let another process try again. 354 355 struct stat keycheck_file_info; 356 if (stat(SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME, &keycheck_file_info) < 0) { 357 int server_fd = socket(PF_UNIX, SOCK_STREAM, 0); 358 if (server_fd < 0) { 359 syslog(LOG_ERR, "Can't get socket (%m) system keychain may be unchecked"); 360 return; 361 } 362 363 struct sockaddr_un keychain_check_server_address; 364 keychain_check_server_address.sun_family = AF_UNIX; 365 if (strlcpy(keychain_check_server_address.sun_path, SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME, sizeof(keychain_check_server_address.sun_path)) > sizeof(keychain_check_server_address.sun_path)) { 366 // It would be nice if we could compile time assert this 367 syslog(LOG_ERR, "Socket path too long, max length %lu, your length %lu", (unsigned long)sizeof(keychain_check_server_address.sun_path), (unsigned long)strlen(SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME)); 368 close(server_fd); 369 return; 370 } 371 keychain_check_server_address.sun_len = SUN_LEN(&keychain_check_server_address); 372 373 int rc = connect(server_fd, (struct sockaddr *)&keychain_check_server_address, keychain_check_server_address.sun_len); 374 if (rc < 0) { 375 syslog(LOG_ERR, "Can not connect to %s: %m", SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME); 376 close(server_fd); 377 return; 378 } 379 380 // this read lets us block until the EOF comes, we don't ever get a byte (and if we do, we don't care about it) 381 char byte; 382 ssize_t read_size = read(server_fd, &byte, 1); 383 if (read_size < 0) { 384 syslog(LOG_ERR, "Error reading from system keychain checker: %m"); 385 } 386 387 close(server_fd); 388 return; 389 } 390} 391 392// 393// KeychainImpl 394// 395KeychainImpl::KeychainImpl(const Db &db) 396 : mInCache(false), mDb(db), mCustomUnlockCreds (this), mIsInBatchMode (false), mMutex(Mutex::recursive) 397{ 398 dispatch_once(&SecKeychainSystemKeychainChecked, ^{ 399 check_system_keychain(); 400 }); 401 mDb->defaultCredentials(this); // install activation hook 402 mEventBuffer = new EventBuffer; 403} 404 405KeychainImpl::~KeychainImpl() 406{ 407 try 408 { 409 // Remove ourselves from the cache if we are in it. 410 // fprintf(stderr, "Removing %p from storage manager cache.\n", handle(false)); 411 globals().storageManager.removeKeychain(dlDbIdentifier(), this); 412 delete mEventBuffer; 413 } 414 catch(...) 415 { 416 } 417} 418 419Mutex* 420KeychainImpl::getMutexForObject() 421{ 422 return globals().storageManager.getStorageManagerMutex(); 423} 424 425Mutex* 426KeychainImpl::getKeychainMutex() 427{ 428 return &mMutex; 429} 430 431void KeychainImpl::aboutToDestruct() 432{ 433 // remove me from the global cache, we are done 434 // fprintf(stderr, "Destructing keychain object\n"); 435 DLDbIdentifier identifier = dlDbIdentifier(); 436 globals().storageManager.removeKeychain(identifier, this); 437} 438 439bool 440KeychainImpl::operator ==(const KeychainImpl &keychain) const 441{ 442 return dlDbIdentifier() == keychain.dlDbIdentifier(); 443} 444 445KCCursor 446KeychainImpl::createCursor(SecItemClass itemClass, const SecKeychainAttributeList *attrList) 447{ 448 StLock<Mutex>_(mMutex); 449 450 StorageManager::KeychainList keychains; 451 keychains.push_back(Keychain(this)); 452 return KCCursor(keychains, itemClass, attrList); 453} 454 455KCCursor 456KeychainImpl::createCursor(const SecKeychainAttributeList *attrList) 457{ 458 StLock<Mutex>_(mMutex); 459 460 StorageManager::KeychainList keychains; 461 keychains.push_back(Keychain(this)); 462 return KCCursor(keychains, attrList); 463} 464 465void 466KeychainImpl::create(UInt32 passwordLength, const void *inPassword) 467{ 468 StLock<Mutex>_(mMutex); 469 470 if (!inPassword) 471 { 472 create(); 473 return; 474 } 475 476 Allocator &alloc = Allocator::standard(); 477 478 // @@@ Share this instance 479 480 const CssmData password(const_cast<void *>(inPassword), passwordLength); 481 AclFactory::PasswordChangeCredentials pCreds (password, alloc); 482 AclFactory::AnyResourceContext rcc(pCreds); 483 create(&rcc); 484} 485 486void KeychainImpl::create(ConstStringPtr inPassword) 487{ 488 StLock<Mutex>_(mMutex); 489 490 if ( inPassword ) 491 create(static_cast<UInt32>(inPassword[0]), &inPassword[1]); 492 else 493 create(); 494} 495 496void 497KeychainImpl::create() 498{ 499 StLock<Mutex>_(mMutex); 500 501 AclFactory aclFactory; 502 AclFactory::AnyResourceContext rcc(aclFactory.unlockCred()); 503 create(&rcc); 504} 505 506void KeychainImpl::createWithBlob(CssmData &blob) 507{ 508 StLock<Mutex>_(mMutex); 509 510 mDb->dbInfo(&Schema::DBInfo); 511 AclFactory aclFactory; 512 AclFactory::AnyResourceContext rcc(aclFactory.unlockCred()); 513 mDb->resourceControlContext (&rcc); 514 try 515 { 516 mDb->createWithBlob(blob); 517 } 518 catch (...) 519 { 520 mDb->resourceControlContext(NULL); 521 mDb->dbInfo(NULL); 522 throw; 523 } 524 mDb->resourceControlContext(NULL); 525 mDb->dbInfo(NULL); // Clear the schema (to not break an open call later) 526 globals().storageManager.created(Keychain(this)); 527 528 KCEventNotifier::PostKeychainEvent (kSecKeychainListChangedEvent, this, NULL); 529} 530 531void 532KeychainImpl::create(const ResourceControlContext *rcc) 533{ 534 StLock<Mutex>_(mMutex); 535 536 mDb->dbInfo(&Schema::DBInfo); // Set the schema (to force a create) 537 mDb->resourceControlContext(rcc); 538 try 539 { 540 mDb->create(); 541 } 542 catch (...) 543 { 544 mDb->resourceControlContext(NULL); 545 mDb->dbInfo(NULL); // Clear the schema (to not break an open call later) 546 throw; 547 } 548 mDb->resourceControlContext(NULL); 549 mDb->dbInfo(NULL); // Clear the schema (to not break an open call later) 550 globals().storageManager.created(Keychain(this)); 551} 552 553void 554KeychainImpl::open() 555{ 556 StLock<Mutex>_(mMutex); 557 558 mDb->open(); 559} 560 561void 562KeychainImpl::lock() 563{ 564 StLock<Mutex>_(mMutex); 565 566 mDb->lock(); 567} 568 569void 570KeychainImpl::unlock() 571{ 572 StLock<Mutex>_(mMutex); 573 574 mDb->unlock(); 575} 576 577void 578KeychainImpl::unlock(const CssmData &password) 579{ 580 StLock<Mutex>_(mMutex); 581 582 mDb->unlock(password); 583} 584 585void 586KeychainImpl::unlock(ConstStringPtr password) 587{ 588 StLock<Mutex>_(mMutex); 589 590 if (password) 591 { 592 const CssmData data(const_cast<unsigned char *>(&password[1]), password[0]); 593 unlock(data); 594 } 595 else 596 unlock(); 597} 598 599void 600KeychainImpl::stash() 601{ 602 StLock<Mutex>_(mMutex); 603 604 mDb->stash(); 605} 606 607void 608KeychainImpl::stashCheck() 609{ 610 StLock<Mutex>_(mMutex); 611 612 mDb->stashCheck(); 613} 614 615void 616KeychainImpl::getSettings(uint32 &outIdleTimeOut, bool &outLockOnSleep) 617{ 618 StLock<Mutex>_(mMutex); 619 620 mDb->getSettings(outIdleTimeOut, outLockOnSleep); 621} 622 623void 624KeychainImpl::setSettings(uint32 inIdleTimeOut, bool inLockOnSleep) 625{ 626 StLock<Mutex>_(mMutex); 627 628 // The .Mac syncing code only makes sense for the AppleFile CSP/DL, 629 // but other DLs such as the OCSP and LDAP DLs do not expose a way to 630 // change settings or the password. To make a minimal change that only affects 631 // the smartcard case, we only look for that CSP/DL 632 633 bool isSmartcard = (mDb->dl()->guid() == gGuidAppleSdCSPDL); 634 635 // get the old keychain blob so that we can tell .Mac to resync it 636 CssmAutoData oldBlob(mDb ->allocator()); 637 if (!isSmartcard) 638 mDb->copyBlob(oldBlob.get()); 639 640 mDb->setSettings(inIdleTimeOut, inLockOnSleep); 641} 642 643void 644KeychainImpl::changePassphrase(UInt32 oldPasswordLength, const void *oldPassword, 645 UInt32 newPasswordLength, const void *newPassword) 646{ 647 StLock<Mutex>_(mMutex); 648 649 bool isSmartcard = (mDb->dl()->guid() == gGuidAppleSdCSPDL); 650 651 TrackingAllocator allocator(Allocator::standard()); 652 AutoCredentials cred = AutoCredentials(allocator); 653 if (oldPassword) 654 { 655 const CssmData &oldPass = *new(allocator) CssmData(const_cast<void *>(oldPassword), oldPasswordLength); 656 TypedList &oldList = *new(allocator) TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK); 657 oldList.append(new(allocator) ListElement(CSSM_SAMPLE_TYPE_PASSWORD)); 658 oldList.append(new(allocator) ListElement(oldPass)); 659 cred += oldList; 660 } 661 662 if (newPassword) 663 { 664 const CssmData &newPass = *new(allocator) CssmData(const_cast<void *>(newPassword), newPasswordLength); 665 TypedList &newList = *new(allocator) TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK); 666 newList.append(new(allocator) ListElement(CSSM_SAMPLE_TYPE_PASSWORD)); 667 newList.append(new(allocator) ListElement(newPass)); 668 cred += newList; 669 } 670 671 // get the old keychain blob so that we can tell .Mac to resync it 672 CssmAutoData oldBlob(mDb->allocator()); 673 if (!isSmartcard) 674 mDb->copyBlob(oldBlob.get()); 675 676 mDb->changePassphrase(&cred); 677} 678 679void 680KeychainImpl::changePassphrase(ConstStringPtr oldPassword, ConstStringPtr newPassword) 681{ 682 StLock<Mutex>_(mMutex); 683 684 const void *oldPtr, *newPtr; 685 UInt32 oldLen, newLen; 686 if (oldPassword) 687 { 688 oldLen = oldPassword[0]; 689 oldPtr = oldPassword + 1; 690 } 691 else 692 { 693 oldLen = 0; 694 oldPtr = NULL; 695 } 696 697 if (newPassword) 698 { 699 newLen = newPassword[0]; 700 newPtr = newPassword + 1; 701 } 702 else 703 { 704 newLen = 0; 705 newPtr = NULL; 706 } 707 708 changePassphrase(oldLen, oldPtr, newLen, newPtr); 709} 710 711void 712KeychainImpl::authenticate(const CSSM_ACCESS_CREDENTIALS *cred) 713{ 714 StLock<Mutex>_(mMutex); 715 716 if (!exists()) 717 MacOSError::throwMe(errSecNoSuchKeychain); 718 719 MacOSError::throwMe(errSecUnimplemented); 720} 721 722UInt32 723KeychainImpl::status() const 724{ 725 // @@@ We should figure out the read/write status though a DL passthrough 726 // or some other way. Also should locked be unlocked read only or just 727 // read-only? 728 return (mDb->isLocked() ? 0 : kSecUnlockStateStatus | kSecWritePermStatus) 729 | kSecReadPermStatus; 730} 731 732bool 733KeychainImpl::exists() 734{ 735 StLock<Mutex>_(mMutex); 736 737 bool exists = true; 738 try 739 { 740 open(); 741 // Ok to leave the mDb open since it will get closed when it goes away. 742 } 743 catch (const CssmError &e) 744 { 745 if (e.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST) 746 throw; 747 exists = false; 748 } 749 750 return exists; 751} 752 753bool 754KeychainImpl::isActive() const 755{ 756 return mDb->isActive(); 757} 758 759void KeychainImpl::completeAdd(Item &inItem, PrimaryKey &primaryKey) 760{ 761 762 763 // The inItem shouldn't be in the cache yet 764 assert(!inItem->inCache()); 765 766 // Insert inItem into mDbItemMap with key primaryKey. p.second will be 767 // true if it got inserted. If not p.second will be false and p.first 768 // will point to the current entry with key primaryKey. 769 pair<DbItemMap::iterator, bool> p = 770 mDbItemMap.insert(DbItemMap::value_type(primaryKey, inItem.get())); 771 if (!p.second) 772 { 773 // There was already an ItemImpl * in mDbItemMap with key 774 // primaryKey. Get a ref to the pointer to it so we can assign a 775 // new value to it below. 776 ItemImpl *oldItem = p.first->second; 777 778 // @@@ If this happens we are breaking our API contract of 779 // uniquifying items. We really need to insert the item into the 780 // map before we start the add. And have the item be in an 781 // "is being added" state. 782 assert(oldItem->inCache()); 783 secdebug("keychain", "add of new item %p somehow replaced %p", 784 inItem.get(), oldItem); 785 786 // make sure that we both mark the item and remove the item from the cache 787 removeItem(oldItem->primaryKey(), oldItem); 788 oldItem = inItem.get(); 789 } 790 791 inItem->inCache(true); 792} 793 794void 795KeychainImpl::addCopy(Item &inItem) 796{ 797 Keychain keychain(this); 798 PrimaryKey primaryKey = inItem->addWithCopyInfo(keychain, true); 799 completeAdd(inItem, primaryKey); 800 postEvent(kSecAddEvent, inItem); 801} 802 803void 804KeychainImpl::add(Item &inItem) 805{ 806 Keychain keychain(this); 807 PrimaryKey primaryKey = inItem->add(keychain); 808 completeAdd(inItem, primaryKey); 809 postEvent(kSecAddEvent, inItem); 810} 811 812void 813KeychainImpl::didUpdate(const Item &inItem, PrimaryKey &oldPK, 814 PrimaryKey &newPK) 815{ 816 // If the primary key hasn't changed we don't need to update mDbItemMap. 817 if (oldPK != newPK) 818 { 819 // If inItem isn't in the cache we don't need to update mDbItemMap. 820 assert(inItem->inCache()); 821 if (inItem->inCache()) 822 { 823 // First remove the entry for inItem in mDbItemMap with key oldPK. 824 DbItemMap::iterator it = mDbItemMap.find(oldPK); 825 if (it != mDbItemMap.end() && (ItemImpl*) it->second == inItem.get()) 826 mDbItemMap.erase(it); 827 828 // Insert inItem into mDbItemMap with key newPK. p.second will be 829 // true if it got inserted. If not p.second will be false and 830 // p.first will point to the current entry with key newPK. 831 pair<DbItemMap::iterator, bool> p = 832 mDbItemMap.insert(DbItemMap::value_type(newPK, inItem.get())); 833 if (!p.second) 834 { 835 // There was already an ItemImpl * in mDbItemMap with key 836 // primaryKey. Get a ref to the pointer to it so we can assign 837 // a new value to it below. 838 ItemImpl *oldItem = p.first->second; 839 840 // @@@ If this happens we are breaking our API contract of 841 // uniquifying items. We really need to insert the item into 842 // the map with the new primary key before we start the update. 843 // And have the item be in an "is being updated" state. 844 assert(oldItem->inCache()); 845 secdebug("keychain", "update of item %p somehow replaced %p", 846 inItem.get(), oldItem); 847 oldItem->inCache(false); 848 oldItem = inItem.get(); 849 } 850 } 851 } 852 853 postEvent(kSecUpdateEvent, inItem); 854} 855 856void 857KeychainImpl::deleteItem(Item &inoutItem) 858{ 859 { 860 // We don't need to hold the DO mutex through event posting, and, in fact, doing so causes deadlock. 861 // Hold it only as long as needed, instead. 862 863 864 // item must be persistent 865 if (!inoutItem->isPersistent()) 866 MacOSError::throwMe(errSecInvalidItemRef); 867 868 DbUniqueRecord uniqueId = inoutItem->dbUniqueRecord(); 869 PrimaryKey primaryKey = inoutItem->primaryKey(); 870 uniqueId->deleteRecord(); 871 872 // Don't remove the item from the mDbItemMap here since this would cause 873 // us to report a new item to our caller when we receive the 874 // kSecDeleteEvent notification. 875 // It will be removed before we post the notification, because 876 // CCallbackMgr will call didDeleteItem() 877 878 // Post the notification for the item deletion with 879 // the primaryKey obtained when the item still existed 880 } 881 882 postEvent(kSecDeleteEvent, inoutItem); 883} 884 885 886CssmClient::CSP 887KeychainImpl::csp() 888{ 889 StLock<Mutex>_(mMutex); 890 891 if (!mDb->dl()->subserviceMask() & CSSM_SERVICE_CSP) 892 MacOSError::throwMe(errSecInvalidKeychain); 893 894 // Try to cast first to a CSPDL to handle case where we don't have an SSDb 895 try 896 { 897 CssmClient::CSPDL cspdl(dynamic_cast<CssmClient::CSPDLImpl *>(&*mDb->dl())); 898 return CSP(cspdl); 899 } 900 catch (...) 901 { 902 SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*mDb)); 903 if (impl == NULL) 904 { 905 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); 906 } 907 908 SSDb ssDb(impl); 909 return ssDb->csp(); 910 } 911} 912 913PrimaryKey 914KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId) 915{ 916 StLock<Mutex>_(mMutex); 917 918 DbAttributes primaryKeyAttrs(uniqueId->database()); 919 primaryKeyAttrs.recordType(recordType); 920 gatherPrimaryKeyAttributes(primaryKeyAttrs); 921 uniqueId->get(&primaryKeyAttrs, NULL); 922 return PrimaryKey(primaryKeyAttrs); 923} 924 925const CssmAutoDbRecordAttributeInfo & 926KeychainImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType) 927{ 928 StLock<Mutex>_(mMutex); 929 930 try 931 { 932 return keychainSchema()->primaryKeyInfosFor(recordType); 933 } 934 catch (const CommonError &error) 935 { 936 switch (error.osStatus()) 937 { 938 case errSecNoSuchClass: 939 case CSSMERR_DL_INVALID_RECORDTYPE: 940 resetSchema(); 941 return keychainSchema()->primaryKeyInfosFor(recordType); 942 default: 943 throw; 944 } 945 } 946} 947 948void KeychainImpl::gatherPrimaryKeyAttributes(DbAttributes& primaryKeyAttrs) 949{ 950 StLock<Mutex>_(mMutex); 951 952 const CssmAutoDbRecordAttributeInfo &infos = 953 primaryKeyInfosFor(primaryKeyAttrs.recordType()); 954 955 // @@@ fix this to not copy info. 956 for (uint32 i = 0; i < infos.size(); i++) 957 primaryKeyAttrs.add(infos.at(i)); 958} 959 960ItemImpl * 961KeychainImpl::_lookupItem(const PrimaryKey &primaryKey) 962{ 963 DbItemMap::iterator it = mDbItemMap.find(primaryKey); 964 if (it != mDbItemMap.end()) 965 { 966 if (it->second == NULL) 967 { 968 // we've been weak released... 969 mDbItemMap.erase(it); 970 } 971 else 972 { 973 return it->second; 974 } 975 } 976 977 return NULL; 978} 979 980Item 981KeychainImpl::item(const PrimaryKey &primaryKey) 982{ 983 StLock<Mutex>_(mMutex); 984 985 // Lookup the item in the map while holding the apiLock. 986 ItemImpl *itemImpl = _lookupItem(primaryKey); 987 if (itemImpl) 988 return Item(itemImpl); 989 990 try 991 { 992 // We didn't find it so create a new item with just a keychain and 993 // a primary key. However since we aren't holding 994 // globals().apiLock anymore some other thread might have beaten 995 // us to creating this item and adding it to the cache. If that 996 // happens we retry the lookup. 997 return Item(this, primaryKey); 998 } 999 catch (const MacOSError &e) 1000 { 1001 // If the item creation failed because some other thread already 1002 // inserted this item into the cache we retry the lookup. 1003 if (e.osStatus() == errSecDuplicateItem) 1004 { 1005 // Lookup the item in the map while holding the apiLock. 1006 ItemImpl *itemImpl = _lookupItem(primaryKey); 1007 if (itemImpl) 1008 return Item(itemImpl); 1009 } 1010 throw; 1011 } 1012} 1013 1014 1015Item 1016KeychainImpl::item(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId) 1017{ 1018 StLock<Mutex>_(mMutex); 1019 1020 PrimaryKey primaryKey = makePrimaryKey(recordType, uniqueId); 1021 { 1022 // Lookup the item in the map while holding the apiLock. 1023 ItemImpl *itemImpl = _lookupItem(primaryKey); 1024 1025 if (itemImpl) 1026 { 1027 return Item(itemImpl); 1028 } 1029 } 1030 1031 try 1032 { 1033 // We didn't find it so create a new item with a keychain, a primary key 1034 // and a DbUniqueRecord. However since we aren't holding 1035 // globals().apiLock anymore some other thread might have beaten 1036 // us to creating this item and adding it to the cache. If that 1037 // happens we retry the lookup. 1038 return Item(this, primaryKey, uniqueId); 1039 } 1040 catch (const MacOSError &e) 1041 { 1042 // If the item creation failed because some other thread already 1043 // inserted this item into the cache we retry the lookup. 1044 if (e.osStatus() == errSecDuplicateItem) 1045 { 1046 // Lookup the item in the map while holding the apiLock. 1047 ItemImpl *itemImpl = _lookupItem(primaryKey); 1048 if (itemImpl) 1049 return Item(itemImpl); 1050 } 1051 throw; 1052 } 1053} 1054 1055KeychainSchema 1056KeychainImpl::keychainSchema() 1057{ 1058 StLock<Mutex>_(mMutex); 1059 if (!mKeychainSchema) 1060 mKeychainSchema = KeychainSchema(mDb); 1061 1062 return mKeychainSchema; 1063} 1064 1065void KeychainImpl::resetSchema() 1066{ 1067 mKeychainSchema = NULL; // re-fetch it from db next time 1068} 1069 1070 1071// Called from DbItemImpl's constructor (so it is only partially constructed), 1072// add it to the map. 1073void 1074KeychainImpl::addItem(const PrimaryKey &primaryKey, ItemImpl *dbItemImpl) 1075{ 1076 StLock<Mutex>_(mMutex); 1077 1078 // The dbItemImpl shouldn't be in the cache yet 1079 assert(!dbItemImpl->inCache()); 1080 1081 // Insert dbItemImpl into mDbItemMap with key primaryKey. p.second will 1082 // be true if it got inserted. If not p.second will be false and p.first 1083 // will point to the current entry with key primaryKey. 1084 pair<DbItemMap::iterator, bool> p = 1085 mDbItemMap.insert(DbItemMap::value_type(primaryKey, dbItemImpl)); 1086 1087 if (!p.second) 1088 { 1089 // There was already an ItemImpl * in mDbItemMap with key primaryKey. 1090 // There is a race condition here when being called in multiple threads 1091 // We might have added an item using add and received a notification at 1092 // the same time. 1093 MacOSError::throwMe(errSecDuplicateItem); 1094 } 1095 1096 dbItemImpl->inCache(true); 1097} 1098 1099void 1100KeychainImpl::didDeleteItem(ItemImpl *inItemImpl) 1101{ 1102 StLock<Mutex>_(mMutex); 1103 1104 // Called by CCallbackMgr 1105 secdebug("kcnotify", "%p notified that item %p was deleted", this, inItemImpl); 1106 removeItem(inItemImpl->primaryKey(), inItemImpl); 1107} 1108 1109void 1110KeychainImpl::removeItem(const PrimaryKey &primaryKey, ItemImpl *inItemImpl) 1111{ 1112 StLock<Mutex>_(mMutex); 1113 1114 // If inItemImpl isn't in the cache to begin with we are done. 1115 if (!inItemImpl->inCache()) 1116 return; 1117 1118 DbItemMap::iterator it = mDbItemMap.find(primaryKey); 1119 if (it != mDbItemMap.end() && (ItemImpl*) it->second == inItemImpl) 1120 mDbItemMap.erase(it); 1121 1122 inItemImpl->inCache(false); 1123} 1124 1125void 1126KeychainImpl::getAttributeInfoForItemID(CSSM_DB_RECORDTYPE itemID, 1127 SecKeychainAttributeInfo **Info) 1128{ 1129 StLock<Mutex>_(mMutex); 1130 1131 try 1132 { 1133 keychainSchema()->getAttributeInfoForRecordType(itemID, Info); 1134 } 1135 catch (const CommonError &error) 1136 { 1137 switch (error.osStatus()) 1138 { 1139 case errSecNoSuchClass: 1140 case CSSMERR_DL_INVALID_RECORDTYPE: 1141 resetSchema(); 1142 keychainSchema()->getAttributeInfoForRecordType(itemID, Info); 1143 default: 1144 throw; 1145 } 1146 } 1147} 1148 1149void 1150KeychainImpl::freeAttributeInfo(SecKeychainAttributeInfo *Info) 1151{ 1152 free(Info->tag); 1153 free(Info->format); 1154 free(Info); 1155} 1156 1157CssmDbAttributeInfo 1158KeychainImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, UInt32 tag) 1159{ 1160 StLock<Mutex>_(mMutex); 1161 1162 try 1163 { 1164 return keychainSchema()->attributeInfoFor(recordType, tag); 1165 } 1166 catch (const CommonError &error) 1167 { 1168 switch (error.osStatus()) 1169 { 1170 case errSecNoSuchClass: 1171 case CSSMERR_DL_INVALID_RECORDTYPE: 1172 resetSchema(); 1173 return keychainSchema()->attributeInfoFor(recordType, tag); 1174 default: 1175 throw; 1176 } 1177 } 1178} 1179 1180void 1181KeychainImpl::recode(const CssmData &data, const CssmData &extraData) 1182{ 1183 StLock<Mutex>_(mMutex); 1184 1185 mDb->recode(data, extraData); 1186} 1187 1188void 1189KeychainImpl::copyBlob(CssmData &data) 1190{ 1191 StLock<Mutex>_(mMutex); 1192 1193 mDb->copyBlob(data); 1194} 1195 1196void 1197KeychainImpl::setBatchMode(Boolean mode, Boolean rollback) 1198{ 1199 StLock<Mutex>_(mMutex); 1200 1201 mDb->setBatchMode(mode, rollback); 1202 mIsInBatchMode = mode; 1203 if (!mode) 1204 { 1205 if (!rollback) // was batch mode being turned off without an abort? 1206 { 1207 // dump the buffer 1208 EventBuffer::iterator it = mEventBuffer->begin(); 1209 while (it != mEventBuffer->end()) 1210 { 1211 PrimaryKey primaryKey; 1212 if (it->item) 1213 { 1214 primaryKey = it->item->primaryKey(); 1215 } 1216 1217 KCEventNotifier::PostKeychainEvent(it->kcEvent, mDb->dlDbIdentifier(), primaryKey); 1218 1219 ++it; 1220 } 1221 1222 } 1223 1224 // notify that a keychain has changed in too many ways to count 1225 KCEventNotifier::PostKeychainEvent(kSecKeychainLeftBatchModeEvent); 1226 mEventBuffer->clear(); 1227 } 1228 else 1229 { 1230 KCEventNotifier::PostKeychainEvent(kSecKeychainEnteredBatchModeEvent); 1231 } 1232} 1233 1234void 1235KeychainImpl::postEvent(SecKeychainEvent kcEvent, ItemImpl* item) 1236{ 1237 PrimaryKey primaryKey; 1238 1239 { 1240 StLock<Mutex>_(mMutex); 1241 1242 if (item != NULL) 1243 { 1244 primaryKey = item->primaryKey(); 1245 } 1246 } 1247 1248 if (!mIsInBatchMode) 1249 { 1250 KCEventNotifier::PostKeychainEvent(kcEvent, mDb->dlDbIdentifier(), primaryKey); 1251 } 1252 else 1253 { 1254 StLock<Mutex>_(mMutex); 1255 1256 EventItem it; 1257 it.kcEvent = kcEvent; 1258 if (item != NULL) 1259 { 1260 it.item = item; 1261 } 1262 1263 mEventBuffer->push_back (it); 1264 } 1265} 1266 1267Keychain::Keychain() 1268{ 1269 dispatch_once(&SecKeychainSystemKeychainChecked, ^{ 1270 check_system_keychain(); 1271 }); 1272} 1273 1274Keychain::~Keychain() 1275{ 1276} 1277 1278 1279 1280Keychain 1281Keychain::optional(SecKeychainRef handle) 1282{ 1283 if (handle) 1284 return KeychainImpl::required(handle); 1285 else 1286 return globals().storageManager.defaultKeychain(); 1287} 1288 1289 1290CFIndex KeychainCore::GetKeychainRetainCount(Keychain& kc) 1291{ 1292 CFTypeRef ref = kc->handle(false); 1293 return CFGetRetainCount(ref); 1294} 1295 1296 1297// 1298// Create default credentials for this keychain. 1299// This is triggered upon default open (i.e. a Db::activate() with no set credentials). 1300// 1301// This function embodies the "default credentials" logic for Keychain-layer databases. 1302// 1303const AccessCredentials * 1304KeychainImpl::makeCredentials() 1305{ 1306 return defaultCredentials(); 1307} 1308 1309 1310const AccessCredentials * 1311KeychainImpl::defaultCredentials() 1312{ 1313 StLock<Mutex>_(mMutex); 1314 1315 // Use custom unlock credentials for file keychains which have a referral 1316 // record and the standard credentials for all others. 1317 1318 if (mDb->dl()->guid() == gGuidAppleCSPDL && mCustomUnlockCreds(mDb)) 1319 return &mCustomUnlockCreds; 1320 else 1321 if (mDb->dl()->guid() == gGuidAppleSdCSPDL) 1322 return globals().smartcardCredentials(); 1323 else 1324 return globals().keychainCredentials(); 1325} 1326 1327 1328 1329bool KeychainImpl::mayDelete() 1330{ 1331 return true; 1332} 1333