dlclient.cpp
1 /* 2 * Copyright (c) 2000-2001,2011-2014 Apple Inc. All Rights Reserved. 3 * 4 * The contents of this file constitute Original Code as defined in and are 5 * subject to the Apple Public Source License Version 1.2 (the 'License'). 6 * You may not use this file except in compliance with the License. Please obtain 7 * a copy of the License at http://www.apple.com/publicsource and read it before 8 * using this file. 9 * 10 * This Original Code and all software distributed under the License are 11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS 12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT 13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the 15 * specific language governing rights and limitations under the License. 16 */ 17 18 19 // 20 // dlclient - client interface to CSSM DLs and their operations 21 // 22 #include <security_cdsa_client/dlclient.h> 23 #include <security_cdsa_client/aclclient.h> 24 #include <Security/cssmapple.h> 25 #include <Security/cssmapplePriv.h> 26 #include <Security/SecBase.h> 27 #include <security_cdsa_utilities/Schema.h> 28 29 using namespace CssmClient; 30 31 #pragma clang diagnostic push 32 #pragma clang diagnostic ignored "-Wunused-const-variable" 33 // blob type for blobs created by these classes -- done so that we can change the formats later 34 const uint32 kBlobType = 0x1; 35 #pragma clang diagnostic pop 36 37 // 38 // Abstract classes 39 // 40 DbMaker::~DbMaker() 41 { /* virtual */ } 42 43 DbCursorMaker::~DbCursorMaker() 44 { /* virtual */ } 45 46 DbUniqueRecordMaker::~DbUniqueRecordMaker() 47 { /* virtual */ } 48 49 50 // 51 // Manage DL attachments 52 // 53 DLImpl::DLImpl(const Guid &guid) : AttachmentImpl(guid, CSSM_SERVICE_DL) 54 { 55 } 56 57 DLImpl::DLImpl(const Module &module) : AttachmentImpl(module, CSSM_SERVICE_DL) 58 { 59 } 60 61 DLImpl::~DLImpl() 62 { 63 } 64 65 void 66 DLImpl::getDbNames(char **) 67 { 68 CssmError::throwMe(CSSMERR_DL_FUNCTION_NOT_IMPLEMENTED); 69 } 70 71 void 72 DLImpl::freeNameList(char **) 73 { 74 CssmError::throwMe(CSSMERR_DL_FUNCTION_NOT_IMPLEMENTED); 75 } 76 77 DbImpl * 78 DLImpl::newDb(const char *inDbName, const CSSM_NET_ADDRESS *inDbLocation) 79 { 80 return new DbImpl(DL(this), inDbName, inDbLocation); 81 } 82 83 84 // 85 // Db (database) 86 // 87 DbImpl::DbImpl(const DL &dl, const char *inDbName, const CSSM_NET_ADDRESS *inDbLocation) 88 : ObjectImpl(dl), mDbName(inDbName, inDbLocation), 89 mUseNameFromHandle(!inDbName), mNameFromHandle(NULL), 90 mAccessRequest(CSSM_DB_ACCESS_READ), mAccessCredentials(NULL), 91 mDefaultCredentials(NULL), mOpenParameters(NULL), mDbInfo(NULL), 92 mResourceControlContext(NULL) 93 { 94 } 95 96 DbImpl::~DbImpl() 97 { 98 try 99 { 100 if (mNameFromHandle) 101 allocator().free(mNameFromHandle); 102 deactivate(); 103 } 104 catch(...) {} 105 } 106 107 void 108 DbImpl::open() 109 { 110 { 111 StLock<Mutex> _(mActivateMutex); 112 if (!mActive) 113 { 114 assert(mDbInfo == nil); 115 mHandle.DLHandle = dl()->handle(); 116 check(CSSM_DL_DbOpen(mHandle.DLHandle, mDbName.canonicalName(), dbLocation(), 117 mAccessRequest, mAccessCredentials, 118 mOpenParameters, &mHandle.DBHandle)); 119 120 mActive = true; 121 } 122 } 123 124 if (!mAccessCredentials && mDefaultCredentials) 125 if (const AccessCredentials *creds = mDefaultCredentials->makeCredentials()) 126 CSSM_DL_Authenticate(handle(), mAccessRequest, creds); // ignore error 127 } 128 129 void 130 DbImpl::createWithBlob(CssmData &blob) 131 { 132 if (mActive) 133 CssmError::throwMe(CSSMERR_DL_DATASTORE_ALREADY_EXISTS); 134 135 if (mDbInfo == nil) { 136 // handle a missing (null) mDbInfo as an all-zero one 137 static const CSSM_DBINFO nullDbInfo = { }; 138 mDbInfo = &nullDbInfo; 139 } 140 141 mHandle.DLHandle = dl()->handle(); 142 143 // create a parameter block for our call to the passthrough 144 CSSM_APPLE_CSPDL_DB_CREATE_WITH_BLOB_PARAMETERS params; 145 146 params.dbName = mDbName.canonicalName (); 147 params.dbLocation = dbLocation (); 148 params.dbInfo = mDbInfo; 149 params.accessRequest = mAccessRequest; 150 params.credAndAclEntry = NULL; 151 params.openParameters = mOpenParameters; 152 params.blob = &blob; 153 154 check(CSSM_DL_PassThrough (mHandle, CSSM_APPLECSPDL_DB_CREATE_WITH_BLOB, ¶ms, (void**) &mHandle.DBHandle)); 155 } 156 157 void 158 DbImpl::create() 159 { 160 StLock<Mutex> _(mActivateMutex); 161 if (mActive) 162 CssmError::throwMe(CSSMERR_DL_DATASTORE_ALREADY_EXISTS); 163 164 if (mDbInfo == nil) { 165 // handle a missing (null) mDbInfo as an all-zero one 166 static const CSSM_DBINFO nullDbInfo = { }; 167 mDbInfo = &nullDbInfo; 168 } 169 mHandle.DLHandle = dl()->handle(); 170 171 if (!mResourceControlContext && mAccessCredentials) { 172 AclFactory::AnyResourceContext ctx(mAccessCredentials); 173 check(CSSM_DL_DbCreate(mHandle.DLHandle, mDbName.canonicalName(), dbLocation(), 174 mDbInfo, mAccessRequest, &ctx, 175 mOpenParameters, &mHandle.DBHandle)); 176 } else { 177 check(CSSM_DL_DbCreate(mHandle.DLHandle, mDbName.canonicalName(), dbLocation(), 178 mDbInfo, mAccessRequest, mResourceControlContext, 179 mOpenParameters, &mHandle.DBHandle)); 180 } 181 mActive = true; 182 } 183 184 void 185 DbImpl::close() 186 { 187 StLock<Mutex> _(mActivateMutex); 188 if (mActive) 189 { 190 check(CSSM_DL_DbClose (mHandle)); 191 mActive = false; 192 } 193 } 194 195 void 196 DbImpl::activate() 197 { 198 StLock<Mutex> _(mActivateMutex); 199 if (!mActive) 200 { 201 if (mDbInfo) 202 create(); 203 else 204 open(); 205 } 206 } 207 208 void 209 DbImpl::deactivate() 210 { 211 StLock<Mutex> _(mActivateMutex); 212 if (mActive) 213 { 214 mActive = false; 215 close(); 216 } 217 } 218 219 void 220 DbImpl::deleteDb() 221 { 222 // Deactivate so the db gets closed if it was open. 223 deactivate(); 224 // This call does not require the receiver to be active. 225 check(CSSM_DL_DbDelete(dl()->handle(), mDbName.canonicalName(), dbLocation(), 226 mAccessCredentials)); 227 } 228 229 void 230 DbImpl::rename(const char *newName) 231 { 232 // Deactivate so the db gets closed if it was open. 233 deactivate(); 234 if (::rename(mDbName.canonicalName(), newName)) 235 UnixError::throwMe(errno); 236 237 // Change our DbName to reflect this rename. 238 mDbName = DbName(newName, dbLocation()); 239 } 240 241 void 242 DbImpl::authenticate(CSSM_DB_ACCESS_TYPE inAccessRequest, 243 const CSSM_ACCESS_CREDENTIALS *inAccessCredentials) 244 { 245 if (!mActive) 246 { 247 // XXX Could do the same for create but this would require sticking 248 // inAccessCredentials into mResourceControlContext. 249 if (!mDbInfo) 250 { 251 // We were not yet active. Just do an open. 252 accessRequest(inAccessRequest); 253 accessCredentials(inAccessCredentials); 254 activate(); 255 return; 256 } 257 } 258 259 check(CSSM_DL_Authenticate(handle(), inAccessRequest, inAccessCredentials)); 260 } 261 262 void 263 DbImpl::name(char *&outDbName) 264 { 265 check(CSSM_DL_GetDbNameFromHandle(handle(), &outDbName)); 266 } 267 268 const char * 269 DbImpl::name() 270 { 271 if (mUseNameFromHandle) 272 { 273 if (mNameFromHandle 274 || !CSSM_DL_GetDbNameFromHandle(handle(), &mNameFromHandle)) 275 { 276 return mNameFromHandle; 277 } 278 279 // We failed to get the name from the handle so use the passed 280 // in name instead 281 mUseNameFromHandle = false; 282 } 283 284 return mDbName.canonicalName(); 285 } 286 287 void 288 DbImpl::createRelation(CSSM_DB_RECORDTYPE inRelationID, 289 const char *inRelationName, 290 uint32 inNumberOfAttributes, 291 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *pAttributeInfo, 292 uint32 inNumberOfIndexes, 293 const CSSM_DB_SCHEMA_INDEX_INFO *pIndexInfo) 294 { 295 check(CSSM_DL_CreateRelation(handle(), inRelationID, inRelationName, 296 inNumberOfAttributes, pAttributeInfo, 297 inNumberOfIndexes, pIndexInfo)); 298 } 299 300 void 301 DbImpl::destroyRelation(CSSM_DB_RECORDTYPE inRelationID) 302 { 303 check(CSSM_DL_DestroyRelation(handle(), inRelationID)); 304 } 305 306 DbUniqueRecord 307 DbImpl::insert(CSSM_DB_RECORDTYPE recordType, const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes, 308 const CSSM_DATA *data) 309 { 310 DbUniqueRecord uniqueId(Db(this)); 311 check(CSSM_DL_DataInsert(handle(), recordType, 312 attributes, 313 data, uniqueId)); 314 // Activate uniqueId so CSSM_DL_FreeUniqueRecord() gets called when it goes out of scope. 315 uniqueId->activate(); 316 return uniqueId; 317 } 318 319 320 DbUniqueRecord 321 DbImpl::insertWithoutEncryption(CSSM_DB_RECORDTYPE recordType, const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes, 322 CSSM_DATA *data) 323 { 324 DbUniqueRecord uniqueId(Db(this)); 325 326 // fill out the parameters 327 CSSM_APPLECSPDL_DB_INSERT_WITHOUT_ENCRYPTION_PARAMETERS params; 328 params.recordType = recordType; 329 params.attributes = const_cast<CSSM_DB_RECORD_ATTRIBUTE_DATA*>(attributes); 330 params.data = *data; 331 332 // for clarity, call the overloaded operator to produce a unique record pointer 333 CSSM_DB_UNIQUE_RECORD_PTR *uniquePtr = uniqueId; 334 335 // make the call 336 passThrough (CSSM_APPLECSPDL_DB_INSERT_WITHOUT_ENCRYPTION, ¶ms, (void**) uniquePtr); 337 338 // Activate uniqueId so CSSM_DL_FreeUniqueRecord() gets called when it goes out of scope. 339 uniqueId->activate(); 340 return uniqueId; 341 } 342 343 344 // 345 // Generic Passthrough interface 346 // 347 void DbImpl::passThrough(uint32 passThroughId, const void *in, void **out) 348 { 349 check(CSSM_DL_PassThrough(handle(), passThroughId, in, out)); 350 } 351 352 353 // 354 // Passthrough functions (only implemented by AppleCSPDL). 355 // 356 void 357 DbImpl::lock() 358 { 359 check(CSSM_DL_PassThrough(handle(), CSSM_APPLECSPDL_DB_LOCK, NULL, NULL)); 360 } 361 362 void 363 DbImpl::unlock() 364 { 365 check(CSSM_DL_PassThrough(handle(), CSSM_APPLECSPDL_DB_UNLOCK, NULL, NULL)); 366 } 367 368 void 369 DbImpl::unlock(const CSSM_DATA &password) 370 { 371 check(CSSM_DL_PassThrough(handle(), CSSM_APPLECSPDL_DB_UNLOCK, &password, NULL)); 372 } 373 374 void 375 DbImpl::stash() 376 { 377 check(CSSM_DL_PassThrough(handle(), CSSM_APPLECSPDL_DB_STASH, NULL, NULL)); 378 } 379 380 void 381 DbImpl::stashCheck() 382 { 383 check(CSSM_DL_PassThrough(handle(), CSSM_APPLECSPDL_DB_STASH_CHECK, NULL, NULL)); 384 } 385 386 void 387 DbImpl::getSettings(uint32 &outIdleTimeout, bool &outLockOnSleep) 388 { 389 CSSM_APPLECSPDL_DB_SETTINGS_PARAMETERS_PTR settings; 390 check(CSSM_DL_PassThrough(handle(), CSSM_APPLECSPDL_DB_GET_SETTINGS, 391 NULL, reinterpret_cast<void **>(&settings))); 392 outIdleTimeout = settings->idleTimeout; 393 outLockOnSleep = settings->lockOnSleep; 394 allocator().free(settings); 395 } 396 397 void 398 DbImpl::setSettings(uint32 inIdleTimeout, bool inLockOnSleep) 399 { 400 CSSM_APPLECSPDL_DB_SETTINGS_PARAMETERS settings; 401 settings.idleTimeout = inIdleTimeout; 402 settings.lockOnSleep = inLockOnSleep; 403 check(CSSM_DL_PassThrough(handle(), CSSM_APPLECSPDL_DB_SET_SETTINGS, &settings, NULL)); 404 } 405 406 bool 407 DbImpl::isLocked() 408 { 409 CSSM_APPLECSPDL_DB_IS_LOCKED_PARAMETERS_PTR params; 410 check(CSSM_DL_PassThrough(handle(), CSSM_APPLECSPDL_DB_IS_LOCKED, 411 NULL, reinterpret_cast<void **>(¶ms))); 412 bool isLocked = params->isLocked; 413 allocator().free(params); 414 return isLocked; 415 } 416 417 void 418 DbImpl::changePassphrase(const CSSM_ACCESS_CREDENTIALS *cred) 419 { 420 CSSM_APPLECSPDL_DB_CHANGE_PASSWORD_PARAMETERS params; 421 params.accessCredentials = const_cast<CSSM_ACCESS_CREDENTIALS *>(cred); 422 check(CSSM_DL_PassThrough(handle(), CSSM_APPLECSPDL_DB_CHANGE_PASSWORD, ¶ms, NULL)); 423 } 424 425 void DbImpl::recode(const CSSM_DATA &data, const CSSM_DATA &extraData) 426 { 427 // setup parameters for the recode call 428 CSSM_APPLECSPDL_RECODE_PARAMETERS params; 429 params.dbBlob = data; 430 params.extraData = extraData; 431 432 // do the call 433 check(CSSM_DL_PassThrough(handle(), CSSM_APPLECSPDL_CSP_RECODE, ¶ms, NULL)); 434 } 435 436 void DbImpl::copyBlob (CssmData &data) 437 { 438 // do the call 439 check(CSSM_DL_PassThrough(handle(), CSSM_APPLECSPDL_DB_COPY_BLOB, NULL, (void**) (CSSM_DATA*) &data)); 440 } 441 442 void DbImpl::setBatchMode(Boolean mode, Boolean rollback) 443 { 444 // 445 // We need the DL_DB_Handle of the underyling DL in order to use CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT 446 // 447 CSSM_RETURN result; 448 CSSM_DL_DB_HANDLE dldbHandleOfUnderlyingDL; 449 result = CSSM_DL_PassThrough(handle(), 450 CSSM_APPLECSPDL_DB_GET_HANDLE, 451 NULL, 452 (void **)&dldbHandleOfUnderlyingDL); 453 // 454 // Now, toggle the autocommit... 455 // 456 if ( result == errSecSuccess ) 457 { 458 CSSM_BOOL modeToUse = !mode; 459 if (rollback) 460 { 461 result = (OSStatus)CSSM_DL_PassThrough(dldbHandleOfUnderlyingDL, 462 CSSM_APPLEFILEDL_ROLLBACK, NULL, NULL); 463 } 464 465 result = CSSM_DL_PassThrough(dldbHandleOfUnderlyingDL, 466 CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT, 467 (void *)((size_t) modeToUse), 468 NULL); 469 if (!rollback && modeToUse) 470 result = CSSM_DL_PassThrough(dldbHandleOfUnderlyingDL, 471 CSSM_APPLEFILEDL_COMMIT, 472 NULL, 473 NULL); 474 } 475 } 476 477 uint32 DbImpl::dbBlobVersion() { 478 uint32 dbBlobVersion = 0; 479 uint32* dbBlobVersionPtr = &dbBlobVersion; 480 481 // We only have a blob version if we're an apple CSPDL 482 if(dl()->guid() == gGuidAppleCSPDL) { 483 check(CSSM_DL_PassThrough(handle(), CSSM_APPLECSPDL_DB_GET_BLOB_VERSION, NULL, (void**) &dbBlobVersionPtr)); 484 } else { 485 secnotice("integrity", "Non-Apple CSPDL keychains don't have keychain versions"); 486 } 487 return dbBlobVersion; 488 } 489 490 uint32 DbImpl::recodeDbToVersion(uint32 version) { 491 uint32 newDbVersion; 492 uint32* newDbVersionPtr = &newDbVersion; 493 check(CSSM_DL_PassThrough(handle(), CSSM_APPLECSPDL_DB_RECODE_TO_BLOB_VERSION, &version, (void**) &newDbVersionPtr)); 494 return newDbVersion; 495 } 496 497 void DbImpl::recodeFinished() { 498 check(CSSM_DL_PassThrough(handle(), CSSM_APPLECSPDL_DB_RECODE_FINISHED, NULL, NULL)); 499 } 500 501 void DbImpl::takeFileLock() { 502 passThrough(CSSM_APPLECSPDL_DB_TAKE_FILE_LOCK, NULL, NULL); 503 } 504 505 void DbImpl::releaseFileLock(bool success) { 506 passThrough(CSSM_APPLECSPDL_DB_RELEASE_FILE_LOCK, &success, NULL); 507 } 508 509 void DbImpl::makeBackup() { 510 passThrough(CSSM_APPLECSPDL_DB_MAKE_BACKUP, NULL, NULL); 511 } 512 513 void DbImpl::makeCopy(const char* path) { 514 passThrough(CSSM_APPLECSPDL_DB_MAKE_COPY, path, NULL); 515 } 516 517 void DbImpl::deleteFile() { 518 passThrough(CSSM_APPLECSPDL_DB_DELETE_FILE, NULL, NULL); 519 } 520 521 void DbImpl::transferTo(const DLDbIdentifier& dldbidentifier) { 522 if (dldbidentifier.ssuid().subserviceType() & CSSM_SERVICE_CSP) { 523 // if we're an Apple CSPDL, do the fancy transfer: 524 // clone the file, clone the db, remove the original file 525 string oldPath = name(); 526 527 CSSM_DB_HANDLE dbhandle; 528 passThrough(CSSM_APPLECSPDL_DB_CLONE, &dldbidentifier, &dbhandle); 529 530 mDbName = dldbidentifier.dbName(); 531 mHandle.DBHandle = dbhandle; 532 533 unlink(oldPath.c_str()); 534 535 // Don't cache this name 536 if (mNameFromHandle) { 537 allocator().free(mNameFromHandle); 538 mNameFromHandle = NULL; 539 } 540 } else { 541 // if we're not an Apple CSPDL, just call rename 542 this->rename(dldbidentifier.dbName()); 543 } 544 } 545 546 547 // cloneTo only makes sense if you're on an Apple CSPDL 548 Db DbImpl::cloneTo(const DLDbIdentifier& dldbidentifier) { 549 CSSM_DB_HANDLE dbhandle; 550 passThrough(CSSM_APPLECSPDL_DB_CLONE, &dldbidentifier, &dbhandle); 551 552 // This is the only reasonable way to make a SSDbImpl at this layer. 553 CssmClient::Db db(dl(), dldbidentifier.dbName(), dldbidentifier.dbLocation()); 554 db->mHandle.DBHandle = dbhandle; 555 556 return db; 557 } 558 559 // 560 // DbCursorMaker 561 // 562 DbCursorImpl * 563 DbImpl::newDbCursor(const CSSM_QUERY &query, Allocator &allocator) 564 { 565 return new DbDbCursorImpl(Db(this), query, allocator); 566 } 567 568 DbCursorImpl * 569 DbImpl::newDbCursor(uint32 capacity, Allocator &allocator) 570 { 571 return new DbDbCursorImpl(Db(this), capacity, allocator); 572 } 573 574 575 // 576 // Db adapters for AclBearer 577 // 578 void DbImpl::getAcl(AutoAclEntryInfoList &aclInfos, const char *selectionTag) const 579 { 580 aclInfos.allocator(allocator()); 581 check(CSSM_DL_GetDbAcl(const_cast<DbImpl*>(this)->handle(), 582 reinterpret_cast<const CSSM_STRING *>(selectionTag), aclInfos, aclInfos)); 583 } 584 585 void DbImpl::changeAcl(const CSSM_ACL_EDIT &aclEdit, 586 const CSSM_ACCESS_CREDENTIALS *accessCred) 587 { 588 check(CSSM_DL_ChangeDbAcl(handle(), AccessCredentials::needed(accessCred), &aclEdit)); 589 } 590 591 void DbImpl::getOwner(AutoAclOwnerPrototype &owner) const 592 { 593 owner.allocator(allocator()); 594 check(CSSM_DL_GetDbOwner(const_cast<DbImpl*>(this)->handle(), owner)); 595 } 596 597 void DbImpl::changeOwner(const CSSM_ACL_OWNER_PROTOTYPE &newOwner, 598 const CSSM_ACCESS_CREDENTIALS *accessCred) 599 { 600 check(CSSM_DL_ChangeDbOwner(handle(), 601 AccessCredentials::needed(accessCred), &newOwner)); 602 } 603 604 void DbImpl::defaultCredentials(DefaultCredentialsMaker *maker) 605 { 606 mDefaultCredentials = maker; 607 } 608 609 610 // 611 // Abstract DefaultCredentialsMakers 612 // 613 DbImpl::DefaultCredentialsMaker::~DefaultCredentialsMaker() 614 { /* virtual */ } 615 616 617 // 618 // Db adapters for DLAccess 619 // 620 CSSM_HANDLE Db::dlGetFirst(const CSSM_QUERY &query, CSSM_DB_RECORD_ATTRIBUTE_DATA &attributes, 621 CSSM_DATA *data, CSSM_DB_UNIQUE_RECORD *&id) 622 { 623 CSSM_HANDLE result; 624 switch (CSSM_RETURN rc = CSSM_DL_DataGetFirst(handle(), &query, &result, &attributes, data, &id)) { 625 case CSSM_OK: 626 return result; 627 case CSSMERR_DL_ENDOFDATA: 628 return CSSM_INVALID_HANDLE; 629 default: 630 CssmError::throwMe(rc); 631 } 632 } 633 634 bool Db::dlGetNext(CSSM_HANDLE query, CSSM_DB_RECORD_ATTRIBUTE_DATA &attributes, 635 CSSM_DATA *data, CSSM_DB_UNIQUE_RECORD *&id) 636 { 637 CSSM_RETURN rc = CSSM_DL_DataGetNext(handle(), query, &attributes, data, &id); 638 switch (rc) { 639 case CSSM_OK: 640 return true; 641 case CSSMERR_DL_ENDOFDATA: 642 return false; 643 default: 644 CssmError::throwMe(rc); 645 } 646 } 647 648 void Db::dlAbortQuery(CSSM_HANDLE query) 649 { 650 CssmError::check(CSSM_DL_DataAbortQuery(handle(), query)); 651 } 652 653 void Db::dlFreeUniqueId(CSSM_DB_UNIQUE_RECORD *id) 654 { 655 CssmError::check(CSSM_DL_FreeUniqueRecord(handle(), id)); 656 } 657 658 void Db::dlDeleteRecord(CSSM_DB_UNIQUE_RECORD *id) 659 { 660 CssmError::check(CSSM_DL_DataDelete(handle(), id)); 661 } 662 663 Allocator &Db::allocator() 664 { 665 return Object::allocator(); 666 } 667 668 669 // 670 // DbUniqueRecordMaker 671 // 672 DbUniqueRecordImpl * 673 DbImpl::newDbUniqueRecord() 674 { 675 return new DbUniqueRecordImpl(Db(this)); 676 } 677 678 679 // 680 // Utility methods 681 // 682 DLDbIdentifier 683 DbImpl::dlDbIdentifier() 684 { 685 // Always use the same dbName and dbLocation that were passed in during 686 // construction 687 return DLDbIdentifier(dl()->subserviceUid(), mDbName.canonicalName(), dbLocation()); 688 } 689 690 691 // 692 // DbDbCursorImpl 693 // 694 DbDbCursorImpl::DbDbCursorImpl(const Db &db, const CSSM_QUERY &query, Allocator &allocator) 695 : DbCursorImpl(db, query, allocator), mResultsHandle(CSSM_INVALID_HANDLE) 696 { 697 } 698 699 DbDbCursorImpl::DbDbCursorImpl(const Db &db, uint32 capacity, Allocator &allocator) 700 : DbCursorImpl(db, capacity, allocator), mResultsHandle(CSSM_INVALID_HANDLE) 701 { 702 } 703 704 DbDbCursorImpl::~DbDbCursorImpl() 705 { 706 try 707 { 708 deactivate(); 709 } 710 catch(...) {} 711 } 712 713 bool 714 DbDbCursorImpl::next(DbAttributes *attributes, ::CssmDataContainer *data, DbUniqueRecord &uniqueId) 715 { 716 if (attributes) 717 attributes->deleteValues(); 718 719 if (data) 720 data->clear(); 721 722 CSSM_RETURN result; 723 Db db(database()); 724 DbUniqueRecord unique(db); 725 if (!mActive) 726 { 727 // ask the CSP/DL if the requested record type exists 728 CSSM_BOOL boolResult; 729 CSSM_DL_PassThrough(db->handle(), CSSM_APPLECSPDL_DB_RELATION_EXISTS, &RecordType, (void**) &boolResult); 730 if (!boolResult) 731 { 732 if (data != NULL) 733 { 734 data->invalidate(); 735 } 736 737 return false; 738 } 739 740 result = CSSM_DL_DataGetFirst(db->handle(), 741 this, 742 &mResultsHandle, 743 attributes, 744 data, 745 unique); 746 747 StLock<Mutex> _(mActivateMutex); 748 if (result == CSSM_OK) 749 mActive = true; 750 else if (data != NULL) 751 data->invalidate (); 752 } 753 else 754 { 755 result = CSSM_DL_DataGetNext(db->handle(), 756 mResultsHandle, 757 attributes, 758 data, 759 unique); 760 761 if (result != CSSM_OK && data != NULL) 762 { 763 data->invalidate (); 764 } 765 } 766 767 if (result != CSSM_OK && attributes != NULL) 768 { 769 attributes->invalidate(); 770 } 771 772 if (result == CSSMERR_DL_ENDOFDATA) 773 { 774 StLock<Mutex> _(mActivateMutex); 775 mActive = false; 776 return false; 777 } 778 779 check(result); 780 781 // Activate uniqueId so CSSM_DL_FreeUniqueRecord() gets called when it goes out of scope. 782 unique->activate(); 783 uniqueId = unique; 784 return true; 785 } 786 787 void 788 DbDbCursorImpl::activate() 789 { 790 } 791 792 void 793 DbDbCursorImpl::deactivate() 794 { 795 StLock<Mutex> _(mActivateMutex); 796 if (mActive) 797 { 798 mActive = false; 799 check(CSSM_DL_DataAbortQuery(database()->handle(), mResultsHandle)); 800 } 801 } 802 803 804 // 805 // DbCursorImpl 806 // 807 DbCursorImpl::DbCursorImpl(const Object &parent, const CSSM_QUERY &query, Allocator &allocator) : 808 ObjectImpl(parent), CssmAutoQuery(query, allocator) 809 { 810 } 811 812 DbCursorImpl::DbCursorImpl(const Object &parent, uint32 capacity, Allocator &allocator) : 813 ObjectImpl(parent), CssmAutoQuery(capacity, allocator) 814 { 815 } 816 817 Allocator & 818 DbCursorImpl::allocator() const 819 { 820 return ObjectImpl::allocator(); 821 } 822 823 void 824 DbCursorImpl::allocator(Allocator &alloc) 825 { 826 ObjectImpl::allocator(alloc); 827 } 828 829 830 // 831 // DbUniqueRecord 832 // 833 DbUniqueRecordImpl::DbUniqueRecordImpl(const Db &db) : ObjectImpl(db), mDestroyID (false) 834 { 835 } 836 837 DbUniqueRecordImpl::~DbUniqueRecordImpl() 838 { 839 try 840 { 841 if (mDestroyID) 842 { 843 allocator ().free (mUniqueId); 844 } 845 846 deactivate(); 847 } 848 catch(...) {} 849 } 850 851 void 852 DbUniqueRecordImpl::deleteRecord() 853 { 854 check(CSSM_DL_DataDelete(database()->handle(), mUniqueId)); 855 } 856 857 void 858 DbUniqueRecordImpl::modify(CSSM_DB_RECORDTYPE recordType, 859 const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes, 860 const CSSM_DATA *data, 861 CSSM_DB_MODIFY_MODE modifyMode) 862 { 863 check(CSSM_DL_DataModify(database()->handle(), recordType, mUniqueId, 864 attributes, 865 data, modifyMode)); 866 } 867 868 void 869 DbUniqueRecordImpl::modifyWithoutEncryption(CSSM_DB_RECORDTYPE recordType, 870 const CSSM_DB_RECORD_ATTRIBUTE_DATA *attributes, 871 const CSSM_DATA *data, 872 CSSM_DB_MODIFY_MODE modifyMode) 873 { 874 // fill out the parameters 875 CSSM_APPLECSPDL_DB_MODIFY_WITHOUT_ENCRYPTION_PARAMETERS params; 876 params.recordType = recordType; 877 params.uniqueID = mUniqueId; 878 params.attributes = const_cast<CSSM_DB_RECORD_ATTRIBUTE_DATA*>(attributes); 879 params.data = (CSSM_DATA*) data; 880 params.modifyMode = modifyMode; 881 882 // modify the data 883 check(CSSM_DL_PassThrough(database()->handle(), 884 CSSM_APPLECSPDL_DB_MODIFY_WITHOUT_ENCRYPTION, 885 ¶ms, 886 NULL)); 887 } 888 889 void 890 DbUniqueRecordImpl::get(DbAttributes *attributes, 891 ::CssmDataContainer *data) 892 { 893 if (attributes) 894 attributes->deleteValues(); 895 896 if (data) 897 data->clear(); 898 899 // @@@ Fix the allocators for attributes and data. 900 CSSM_RETURN result; 901 result = CSSM_DL_DataGetFromUniqueRecordId(database()->handle(), mUniqueId, 902 attributes, 903 data); 904 905 if (result != CSSM_OK) 906 { 907 if (attributes) 908 attributes->invalidate(); 909 if (data != NULL) // the data returned is no longer valid 910 { 911 data->invalidate (); 912 } 913 } 914 915 check(result); 916 } 917 918 void 919 DbUniqueRecordImpl::getWithoutEncryption(DbAttributes *attributes, 920 ::CssmDataContainer *data) 921 { 922 if (attributes) 923 attributes->deleteValues(); 924 925 if (data) 926 data->clear(); 927 928 // @@@ Fix the allocators for attributes and data. 929 CSSM_RETURN result; 930 931 // make the parameter block 932 CSSM_APPLECSPDL_DB_GET_WITHOUT_ENCRYPTION_PARAMETERS params; 933 params.uniqueID = mUniqueId; 934 params.attributes = attributes; 935 936 // get the data 937 ::CssmDataContainer recordData; 938 result = CSSM_DL_PassThrough(database()->handle(), CSSM_APPLECSPDL_DB_GET_WITHOUT_ENCRYPTION, ¶ms, 939 (void**) data); 940 check (result); 941 } 942 943 void 944 DbUniqueRecordImpl::activate() 945 { 946 StLock<Mutex> _(mActivateMutex); 947 mActive = true; 948 } 949 950 void 951 DbUniqueRecordImpl::deactivate() 952 { 953 StLock<Mutex> _(mActivateMutex); 954 if (mActive) 955 { 956 mActive = false; 957 check(CSSM_DL_FreeUniqueRecord(database()->handle(), mUniqueId)); 958 } 959 } 960 961 void 962 DbUniqueRecordImpl::getRecordIdentifier(CSSM_DATA &data) 963 { 964 check(CSSM_DL_PassThrough(database()->handle(), CSSM_APPLECSPDL_DB_GET_RECORD_IDENTIFIER, 965 mUniqueId, (void**) &data)); 966 } 967 968 void DbUniqueRecordImpl::setUniqueRecordPtr(CSSM_DB_UNIQUE_RECORD_PTR uniquePtr) 969 { 970 // clone the record 971 mUniqueId = (CSSM_DB_UNIQUE_RECORD_PTR) allocator ().malloc (sizeof (CSSM_DB_UNIQUE_RECORD)); 972 *mUniqueId = *uniquePtr; 973 mDestroyID = true; 974 } 975 976 // 977 // DbAttributes 978 // 979 DbAttributes::DbAttributes() 980 : CssmAutoDbRecordAttributeData(0, Allocator::standard(), Allocator::standard()) 981 { 982 } 983 984 DbAttributes::DbAttributes(const Db &db, uint32 capacity, Allocator &allocator) 985 : CssmAutoDbRecordAttributeData(capacity, db->allocator(), allocator) 986 { 987 } 988 989 void DbAttributes::updateWithDbAttributes(DbAttributes* newValues) { 990 if(!newValues) { 991 return; 992 } 993 994 canonicalize(); 995 newValues->canonicalize(); 996 997 updateWith(newValues); 998 } 999 1000 void 1001 DbAttributes::canonicalize() { 1002 for(int i = 0; i < size(); i++) { 1003 CssmDbAttributeData& data = attributes()[i]; 1004 CssmDbAttributeInfo& datainfo = data.info(); 1005 1006 // Calling Schema::attributeInfo is the best way to canonicalize. 1007 // There's no way around the try-catch structure, since it throws if it 1008 // can't find something. 1009 1010 try { 1011 if(datainfo.nameFormat() == CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER) { 1012 data.info() = Security::KeychainCore::Schema::attributeInfo(datainfo.intName()); 1013 } 1014 } catch(...) { 1015 // Don't worry about it 1016 } 1017 } 1018 }