1/* 2 * Copyright (c) 2000-2008,2013 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// tokendatabase - software database container implementation. 27// 28#include "tokendatabase.h" 29#include "tokenkey.h" 30#include "tokenaccess.h" 31#include "process.h" 32#include "server.h" 33#include "localkey.h" // to retrieve local raw keys 34#include <security_cdsa_client/wrapkey.h> 35 36 37// 38// Construct a TokenDbCommon 39// 40TokenDbCommon::TokenDbCommon(Session &ssn, Token &tk, const char *name) 41 : DbCommon(ssn), mDbName(name ? name : ""), mHasAclState(false), mResetLevel(0) 42{ 43 secdebug("tokendb", "creating tokendbcommon %p: with token %p", this, &tk); 44 parent(tk); 45} 46 47TokenDbCommon::~TokenDbCommon() 48{ 49 secdebug("tokendb", "destroying tokendbcommon %p", this); 50 token().removeCommon(*this); // unregister from Token 51} 52 53Token &TokenDbCommon::token() const 54{ 55 return parent<Token>(); 56} 57 58std::string TokenDbCommon::dbName() const 59{ 60 return token().printName(); 61} 62 63 64// 65// A TokenDbCommon holds per-session adornments for the ACL machine 66// 67Adornable &TokenDbCommon::store() 68{ 69 StLock<Mutex> _(*this); 70 71 // if this is the first one, hook for lifetime 72 if (!mHasAclState) { 73 session().addReference(*this); // hold and slave to SSN lifetime 74 token().addCommon(*this); // register with Token 75 mHasAclState = true; 76 } 77 78 // return our (now active) adornments 79 return *this; 80} 81 82void TokenDbCommon::resetAcls() 83{ 84 StLock<Mutex> _(*this); 85 if (mHasAclState) { 86 clearAdornments(); // clear ACL state 87 session().removeReference(*this); // unhook from SSN 88 mHasAclState = false; 89 } 90 token().removeCommon(*this); // unregister from Token 91} 92 93 94// 95// Send out a "keychain" notification for this database 96// 97void TokenDbCommon::notify(NotificationEvent event) 98{ 99 DbCommon::notify(event, DLDbIdentifier(dbName().c_str(), gGuidAppleSdCSPDL, 100 subservice(), CSSM_SERVICE_DL | CSSM_SERVICE_CSP)); 101} 102 103 104// 105// Process (our part of) a "lock all" request. 106// Smartcard tokens interpret a "lock" as a forced card reset, transmitted 107// to tokend as an authenticate request. 108// @@@ Virtual reset for multi-session tokens. Right now, we're using the sledge hammer. 109// 110void TokenDbCommon::lockProcessing() 111{ 112 Access access(token()); 113 access().authenticate(CSSM_DB_ACCESS_RESET, NULL); 114} 115 116// 117// Construct a TokenDatabase given subservice information. 118// We are currently ignoring the 'name' argument. 119// 120TokenDatabase::TokenDatabase(uint32 ssid, Process &proc, 121 const char *name, const AccessCredentials *cred) 122 : Database(proc) 123{ 124 // locate Token object 125 RefPointer<Token> token = Token::find(ssid); 126 127 Session &session = process().session(); 128 StLock<Mutex> _(session); 129 if (TokenDbCommon *dbcom = session.findFirst<TokenDbCommon, uint32>(&TokenDbCommon::subservice, ssid)) { 130 parent(*dbcom); 131 secdebug("tokendb", "open tokendb %p(%d) at known common %p", 132 this, subservice(), dbcom); 133 } else { 134 // DbCommon not present; make a new one 135 parent(*new TokenDbCommon(proc.session(), *token, name)); 136 secdebug("tokendb", "open tokendb %p(%d) with new common %p", 137 this, subservice(), &common()); 138 } 139 mOpenCreds = copy(cred, Allocator::standard()); 140 proc.addReference(*this); 141} 142 143TokenDatabase::~TokenDatabase() 144{ 145 Allocator::standard().free(mOpenCreds); 146} 147 148 149// 150// Basic Database virtual implementations 151// 152TokenDbCommon &TokenDatabase::common() const 153{ 154 return parent<TokenDbCommon>(); 155} 156 157TokenDaemon &TokenDatabase::tokend() 158{ 159 return common().token().tokend(); 160} 161 162const char *TokenDatabase::dbName() const 163{ 164 //store dbName to ensure that will live outside function scope 165 mDbName = common().dbName(); 166 return mDbName.c_str(); 167} 168 169bool TokenDatabase::transient() const 170{ 171 //@@@ let tokend decide? Are there any secure transient keystores? 172 return false; 173} 174 175 176// 177// Our ObjectAcl resides in the Token object. 178// 179SecurityServerAcl &TokenDatabase::acl() 180{ 181 return token(); 182} 183 184 185// 186// We post-process the status version of getAcl to account for virtual (per-session) 187// PIN lock status. 188// 189void TokenDatabase::getAcl(const char *tag, uint32 &count, AclEntryInfo *&acls) 190{ 191 AclSource::getAcl(tag, count, acls); 192 193 for (unsigned n = 0; n < count; n++) { 194 AclEntryPrototype &proto = acls[n]; 195 if (unsigned pin = pinFromAclTag(proto.tag(), "?")) { // pin state response 196 secdebug("tokendb", "%p updating PIN%d state response", this, pin); 197 TypedList &subject = proto.subject(); 198 // subject == { CSSM_WORID_PIN, pin-number, status [, count ] } # all numbers 199 if (subject.length() > 2 200 && subject[0].is(CSSM_LIST_ELEMENT_WORDID) 201 && subject[0] == CSSM_WORDID_PIN 202 && subject[1].is(CSSM_LIST_ELEMENT_WORDID) 203 && subject[2].is(CSSM_LIST_ELEMENT_WORDID)) { 204 uint32 pin = subject[1]; 205 if (!common().attachment<PreAuthorizationAcls::AclState>((void *)pin).accepted) { 206 // we are not pre-authorized in this session 207 secdebug("tokendb", "%p session state forces PIN%d reporting unauthorized", this, pin); 208 uint32 status = subject[2]; 209 status &= ~CSSM_ACL_PREAUTH_TRACKING_AUTHORIZED; // clear authorized bit 210 subject[2] = status; 211#if !defined(NDEBUG) 212 if (subject.length() > 3 && subject[3].is(CSSM_LIST_ELEMENT_WORDID)) 213 secdebug("tokendb", "%p PIN%d count=%d", this, pin, subject[3].word()); 214#endif //NDEBUG 215 } 216 } 217 } 218 } 219} 220 221 222bool TokenDatabase::isLocked() 223{ 224 Access access(token()); 225 226 bool lockState = pinState(1); 227// bool lockState = access().isLocked(); 228 229 secdebug("tokendb", "returning isLocked=%d", lockState); 230 return lockState; 231} 232 233bool TokenDatabase::pinState(uint32 pin, int *pinCount /* = NULL */) 234{ 235 uint32 count; 236 AclEntryInfo *acls; 237 this->getAcl("PIN1?", count, acls); 238 bool locked = true; // preset locked 239 if (pinCount) 240 *pinCount = -1; // preset unknown 241 switch (count) { 242 case 0: 243 secdebug("tokendb", "PIN%d query returned no entries", pin); 244 break; 245 default: 246 secdebug("tokendb", "PIN%d query returned multiple entries", pin); 247 break; 248 case 1: 249 { 250 TypedList &subject = acls[0].proto().subject(); 251 if (subject.length() > 2 252 && subject[0].is(CSSM_LIST_ELEMENT_WORDID) 253 && subject[0] == CSSM_WORDID_PIN 254 && subject[1].is(CSSM_LIST_ELEMENT_WORDID) 255 && subject[2].is(CSSM_LIST_ELEMENT_WORDID)) { 256 uint32 status = subject[2]; 257 locked = !(status & CSSM_ACL_PREAUTH_TRACKING_AUTHORIZED); 258 if (pinCount && locked && subject.length() > 3 && subject[3].is(CSSM_LIST_ELEMENT_WORDID)) 259 *pinCount = subject[3]; 260 } 261 } 262 break; 263 } 264 265 // release memory allocated by getAcl 266 ChunkFreeWalker free; 267 for (uint32 n = 0; n < count; n++) 268 walk(free, acls[n]); 269 Allocator::standard().free(acls); 270 271 // return status 272 return locked; 273} 274 275 276// 277// TokenDatabases implement the dbName-setting function. 278// This sets the print name of the token, which is persistently 279// stored in the token cache. So this is a de-facto rename of 280// the token, at least on this system. 281// 282void TokenDatabase::dbName(const char *name) 283{ 284 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED); 285} 286 287 288// 289// Given a key handle and CssmKey returned from tokend, create a Key representing 290// it. This takes care of raw returns by turning them into keys of the process's 291// local transient store. 292// 293RefPointer<Key> TokenDatabase::makeKey(KeyHandle hKey, const CssmKey *key, 294 uint32 moreAttributes, const AclEntryPrototype *owner) 295{ 296 switch (key->blobType()) { 297 case CSSM_KEYBLOB_REFERENCE: 298 return new TokenKey(*this, hKey, key->header()); 299 case CSSM_KEYBLOB_RAW: 300 return process().makeTemporaryKey(*key, moreAttributes, owner); 301 default: 302 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); // bad key return from tokend 303 } 304 //@@@ Server::releaseWhenDone(key); 305} 306 307 308// 309// Adjust key attributes for newly created keys 310// 311static CSSM_KEYATTR_FLAGS modattrs(CSSM_KEYATTR_FLAGS attrs) 312{ 313 static const CSSM_KEYATTR_FLAGS CSSM_KEYATTR_RETURN_FLAGS = 0xff000000; 314 switch (attrs & CSSM_KEYATTR_RETURN_FLAGS) { 315 case CSSM_KEYATTR_RETURN_REF: 316 case CSSM_KEYATTR_RETURN_DATA: 317 break; // as requested 318 case CSSM_KEYATTR_RETURN_NONE: 319 CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYATTR_MASK); 320 case CSSM_KEYATTR_RETURN_DEFAULT: 321 if (attrs & CSSM_KEYATTR_PERMANENT) 322 attrs |= CSSM_KEYATTR_RETURN_REF; 323 else 324 attrs |= CSSM_KEYATTR_RETURN_DATA; 325 break; 326 } 327 return attrs; 328} 329 330 331// 332// TokenDatabases support remote secret validation by sending a secret 333// (aka passphrase et al) to tokend for processing. 334// 335bool TokenDatabase::validateSecret(const AclSubject *subject, const AccessCredentials *cred) 336{ 337 secdebug("tokendb", "%p attempting remote validation", this); 338 try { 339 Access access(token()); 340 // @@@ Use cached mode 341 access().authenticate(CSSM_DB_ACCESS_READ, cred); 342 secdebug("tokendb", "%p remote validation successful", this); 343 return true; 344 } 345 catch (...) { 346 secdebug("tokendb", "%p remote validation failed", this); 347 // return false; 348 throw; // try not to mask error 349 } 350} 351 352 353// 354// Key inquiries 355// 356void TokenDatabase::queryKeySizeInBits(Key &key, CssmKeySize &result) 357{ 358 Access access(token()); 359 TRY 360 GUARD 361 access().queryKeySizeInBits(myKey(key).tokenHandle(), result); 362 DONE 363} 364 365 366// 367// Signatures and MACs 368// 369void TokenDatabase::generateSignature(const Context &context, Key &key, 370 CSSM_ALGORITHMS signOnlyAlgorithm, const CssmData &data, CssmData &signature) 371{ 372 Access access(token(), key); 373 TRY 374 key.validate(CSSM_ACL_AUTHORIZATION_SIGN, context); 375 GUARD 376 access().generateSignature(context, myKey(key).tokenHandle(), data, signature, signOnlyAlgorithm); 377 DONE 378} 379 380 381void TokenDatabase::verifySignature(const Context &context, Key &key, 382 CSSM_ALGORITHMS verifyOnlyAlgorithm, const CssmData &data, const CssmData &signature) 383{ 384 Access access(token(), key); 385 TRY 386 GUARD 387 access().verifySignature(context, myKey(key).tokenHandle(), data, signature, verifyOnlyAlgorithm); 388 DONE 389} 390 391void TokenDatabase::generateMac(const Context &context, Key &key, 392 const CssmData &data, CssmData &mac) 393{ 394 Access access(token()); 395 TRY 396 key.validate(CSSM_ACL_AUTHORIZATION_MAC, context); 397 GUARD 398 access().generateMac(context, myKey(key).tokenHandle(), data, mac); 399 DONE 400} 401 402void TokenDatabase::verifyMac(const Context &context, Key &key, 403 const CssmData &data, const CssmData &mac) 404{ 405 Access access(token()); 406 TRY 407 key.validate(CSSM_ACL_AUTHORIZATION_MAC, context); 408 GUARD 409 access().verifyMac(context, myKey(key).tokenHandle(), data, mac); 410 DONE 411} 412 413 414// 415// Encryption/decryption 416// 417void TokenDatabase::encrypt(const Context &context, Key &key, 418 const CssmData &clear, CssmData &cipher) 419{ 420 Access access(token()); 421 TRY 422 key.validate(CSSM_ACL_AUTHORIZATION_ENCRYPT, context); 423 GUARD 424 access().encrypt(context, myKey(key).tokenHandle(), clear, cipher); 425 DONE 426} 427 428 429void TokenDatabase::decrypt(const Context &context, Key &key, 430 const CssmData &cipher, CssmData &clear) 431{ 432 Access access(token()); 433 TRY 434 key.validate(CSSM_ACL_AUTHORIZATION_DECRYPT, context); 435 GUARD 436 access().decrypt(context, myKey(key).tokenHandle(), cipher, clear); 437 DONE 438} 439 440 441// 442// Key generation and derivation. 443// Currently, we consider symmetric key generation to be fast, but 444// asymmetric key generation to be (potentially) slow. 445// 446void TokenDatabase::generateKey(const Context &context, 447 const AccessCredentials *cred, const AclEntryPrototype *owner, 448 CSSM_KEYUSE usage, CSSM_KEYATTR_FLAGS attrs, RefPointer<Key> &newKey) 449{ 450 Access access(token()); 451 TRY 452 GUARD 453 KeyHandle hKey; 454 CssmKey *result; 455 access().generateKey(context, cred, owner, usage, modattrs(attrs), hKey, result); 456 newKey = makeKey(hKey, result, 0, owner); 457 DONE 458} 459 460void TokenDatabase::generateKey(const Context &context, 461 const AccessCredentials *cred, const AclEntryPrototype *owner, 462 CSSM_KEYUSE pubUsage, CSSM_KEYATTR_FLAGS pubAttrs, 463 CSSM_KEYUSE privUsage, CSSM_KEYATTR_FLAGS privAttrs, 464 RefPointer<Key> &publicKey, RefPointer<Key> &privateKey) 465{ 466 Access access(token()); 467 TRY 468 GUARD 469 KeyHandle hPrivate, hPublic; 470 CssmKey *privKey, *pubKey; 471 access().generateKey(context, cred, owner, 472 pubUsage, modattrs(pubAttrs), privUsage, modattrs(privAttrs), 473 hPublic, pubKey, hPrivate, privKey); 474 publicKey = makeKey(hPublic, pubKey, 0, owner); 475 privateKey = makeKey(hPrivate, privKey, 0, owner); 476 DONE 477} 478 479 480// 481// Key wrapping and unwrapping. 482// Note that the key argument (the key in the context) is optional because of the special 483// case of "cleartext" (null algorithm) wrapping for import/export. 484// 485void TokenDatabase::wrapKey(const Context &context, const AccessCredentials *cred, 486 Key *wrappingKey, Key &subjectKey, 487 const CssmData &descriptiveData, CssmKey &wrappedKey) 488{ 489 Access access(token()); 490 InputKey cWrappingKey(wrappingKey); 491 InputKey cSubjectKey(subjectKey); 492 TRY 493 subjectKey.validate(context.algorithm() == CSSM_ALGID_NONE ? 494 CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR : CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, 495 cred); 496 if (wrappingKey) 497 wrappingKey->validate(CSSM_ACL_AUTHORIZATION_ENCRYPT, context); 498 GUARD 499 CssmKey *rWrappedKey; 500 access().wrapKey(context, cred, 501 cWrappingKey, cWrappingKey, cSubjectKey, cSubjectKey, 502 descriptiveData, rWrappedKey); 503 wrappedKey = *rWrappedKey; 504 //@@@ ownership of wrappedKey.keyData() ?? 505 DONE 506} 507 508void TokenDatabase::unwrapKey(const Context &context, 509 const AccessCredentials *cred, const AclEntryPrototype *owner, 510 Key *wrappingKey, Key *publicKey, CSSM_KEYUSE usage, CSSM_KEYATTR_FLAGS attrs, 511 const CssmKey wrappedKey, RefPointer<Key> &unwrappedKey, CssmData &descriptiveData) 512{ 513 Access access(token()); 514 InputKey cWrappingKey(wrappingKey); 515 InputKey cPublicKey(publicKey); 516 TRY 517 if (wrappingKey) 518 wrappingKey->validate(CSSM_ACL_AUTHORIZATION_DECRYPT, context); 519 // we are not checking access on the public key, if any 520 GUARD 521 KeyHandle hKey; 522 CssmKey *result; 523 access().unwrapKey(context, cred, owner, 524 cWrappingKey, cWrappingKey, cPublicKey, cPublicKey, 525 wrappedKey, usage, modattrs(attrs), descriptiveData, hKey, result); 526 unwrappedKey = makeKey(hKey, result, modattrs(attrs) & LocalKey::managedAttributes, owner); 527 DONE 528} 529 530 531// 532// Key derivation 533// 534void TokenDatabase::deriveKey(const Context &context, Key *sourceKey, 535 const AccessCredentials *cred, const AclEntryPrototype *owner, 536 CssmData *param, CSSM_KEYUSE usage, CSSM_KEYATTR_FLAGS attrs, RefPointer<Key> &derivedKey) 537{ 538 Access access(token()); 539 InputKey cSourceKey(sourceKey); 540 TRY 541 if (sourceKey) 542 sourceKey->validate(CSSM_ACL_AUTHORIZATION_DERIVE, cred); 543 GUARD 544 KeyHandle hKey; 545 CssmKey *result; 546 CssmData params = param ? *param : CssmData(); 547 access().deriveKey(noDb, context, 548 cSourceKey, cSourceKey, 549 usage, modattrs(attrs), params, cred, owner, 550 hKey, result); 551 if (param) { 552 *param = params; 553 //@@@ leak? what's the rule here? 554 } 555 derivedKey = makeKey(hKey, result, 0, owner); 556 DONE 557} 558 559 560// 561// Miscellaneous CSSM functions 562// 563void TokenDatabase::getOutputSize(const Context &context, Key &key, 564 uint32 inputSize, bool encrypt, uint32 &result) 565{ 566 Access access(token()); 567 TRY 568 GUARD 569 access().getOutputSize(context, myKey(key).tokenHandle(), inputSize, encrypt, result); 570 DONE 571} 572 573 574// 575// (Re-)Authenticate the database. 576// We use dbAuthenticate as the catch-all "do something about authentication" call. 577// 578void TokenDatabase::authenticate(CSSM_DB_ACCESS_TYPE mode, const AccessCredentials *cred) 579{ 580 Access access(token()); 581 TRY 582 GUARD 583 if (mode != CSSM_DB_ACCESS_RESET && cred) { 584 secdebug("tokendb", "%p authenticate calling validate", this); 585 if (unsigned pin = pinFromAclTag(cred->EntryTag)) { 586 validate(CSSM_ACL_AUTHORIZATION_PREAUTH(pin), cred); 587 notify(kNotificationEventUnlocked); 588 return; 589 } 590 } 591 592 access().authenticate(mode, cred); 593 switch (mode) { 594 case CSSM_DB_ACCESS_RESET: 595 // this mode is known to trigger "lockdown" (i.e. reset) 596 common().resetAcls(); 597 notify(kNotificationEventLocked); 598 break; 599 default: 600 { 601 // no idea what that did to the token; 602 // But let's remember the new creds for our own sake. 603 AccessCredentials *newCred = copy(cred, Allocator::standard()); 604 Allocator::standard().free(mOpenCreds); 605 mOpenCreds = newCred; 606 } 607 break; 608 } 609 DONE 610} 611 612// 613// Data access interface. 614// 615// Note that the attribute vectors are passed between our two IPC interfaces 616// as relocated but contiguous memory blocks (to avoid an extra copy). This means 617// you can read them at will, but can't change them in transit unless you're 618// willing to repack them right here. 619// 620void TokenDatabase::findFirst(const CssmQuery &query, 621 CssmDbRecordAttributeData *inAttributes, mach_msg_type_number_t inAttributesLength, 622 CssmData *data, RefPointer<Key> &key, 623 RefPointer<Database::Search> &rSearch, RefPointer<Database::Record> &rRecord, 624 CssmDbRecordAttributeData * &outAttributes, mach_msg_type_number_t &outAttributesLength) 625{ 626 Access access(token()); 627 RefPointer<Search> search = new Search(*this); 628 RefPointer<Record> record = new Record(*this); 629 TRY 630 KeyHandle hKey = noKey; 631 validate(CSSM_ACL_AUTHORIZATION_DB_READ, openCreds()); 632 GUARD 633 record->tokenHandle() = access().Tokend::ClientSession::findFirst(query, 634 inAttributes, inAttributesLength, search->tokenHandle(), NULL, hKey, 635 outAttributes, outAttributesLength); 636 if (!record->tokenHandle()) { // no match (but no other error) 637 rRecord = NULL; // return null record 638 return; 639 } 640 if (data) { 641 if (!hKey) 642 record->validate(CSSM_ACL_AUTHORIZATION_DB_READ, openCreds()); 643 CssmDbRecordAttributeData *noAttributes; 644 mach_msg_type_number_t noAttributesLength; 645 access().Tokend::ClientSession::findRecordHandle(record->tokenHandle(), 646 NULL, 0, data, hKey, noAttributes, noAttributesLength); 647 if (hKey) { // tokend returned a key reference & data 648 CssmKey &keyForm = *data->interpretedAs<CssmKey>(CSSMERR_CSP_INVALID_KEY); 649 key = new TokenKey(*this, hKey, keyForm.header()); 650 } 651 } 652 rSearch = search->commit(); 653 rRecord = record->commit(); 654 DONE 655} 656 657void TokenDatabase::findNext(Database::Search *rSearch, 658 CssmDbRecordAttributeData *inAttributes, mach_msg_type_number_t inAttributesLength, 659 CssmData *data, RefPointer<Key> &key, RefPointer<Database::Record> &rRecord, 660 CssmDbRecordAttributeData * &outAttributes, mach_msg_type_number_t &outAttributesLength) 661{ 662 Access access(token()); 663 RefPointer<Record> record = new Record(*this); 664 Search *search = safe_cast<Search *>(rSearch); 665 TRY 666 KeyHandle hKey = noKey; 667 validate(CSSM_ACL_AUTHORIZATION_DB_READ, openCreds()); 668 GUARD 669 record->tokenHandle() = access().Tokend::ClientSession::findNext( 670 search->tokenHandle(), inAttributes, inAttributesLength, 671 NULL, hKey, outAttributes, outAttributesLength); 672 if (!record->tokenHandle()) { // no more matches 673 releaseSearch(*search); // release search handle (consumed by EOD) 674 rRecord = NULL; // return null record 675 return; 676 } 677 if (data) { 678 if (!hKey) 679 record->validate(CSSM_ACL_AUTHORIZATION_DB_READ, openCreds()); 680 CssmDbRecordAttributeData *noAttributes; 681 mach_msg_type_number_t noAttributesLength; 682 access().Tokend::ClientSession::findRecordHandle(record->tokenHandle(), 683 NULL, 0, data, hKey, noAttributes, noAttributesLength); 684 if (hKey) { // tokend returned a key reference & data 685 CssmKey &keyForm = *data->interpretedAs<CssmKey>(CSSMERR_CSP_INVALID_KEY); 686 key = new TokenKey(*this, hKey, keyForm.header()); 687 } 688 } 689 rRecord = record->commit(); 690 DONE 691} 692 693void TokenDatabase::findRecordHandle(Database::Record *rRecord, 694 CssmDbRecordAttributeData *inAttributes, mach_msg_type_number_t inAttributesLength, 695 CssmData *data, RefPointer<Key> &key, 696 CssmDbRecordAttributeData * &outAttributes, mach_msg_type_number_t &outAttributesLength) 697{ 698 Access access(token()); 699 Record *record = safe_cast<Record *>(rRecord); 700 access.add(*record); 701 TRY 702 KeyHandle hKey = noKey; 703 validate(CSSM_ACL_AUTHORIZATION_DB_READ, openCreds()); 704 if (data) 705 record->validate(CSSM_ACL_AUTHORIZATION_DB_READ, openCreds()); 706 GUARD 707 access().Tokend::ClientSession::findRecordHandle(record->tokenHandle(), 708 inAttributes, inAttributesLength, data, hKey, outAttributes, outAttributesLength); 709 rRecord = record; 710 if (hKey != noKey && data) { // tokend returned a key reference & data 711 CssmKey &keyForm = *data->interpretedAs<CssmKey>(CSSMERR_CSP_INVALID_KEY); 712 key = new TokenKey(*this, hKey, keyForm.header()); 713 } 714 DONE 715} 716 717void TokenDatabase::insertRecord(CSSM_DB_RECORDTYPE recordType, 718 const CssmDbRecordAttributeData *attributes, mach_msg_type_number_t attributesLength, 719 const CssmData &data, RefPointer<Database::Record> &rRecord) 720{ 721 Access access(token()); 722 RefPointer<Record> record = new Record(*this); 723 access.add(*record); 724 TRY 725 validate(CSSM_ACL_AUTHORIZATION_DB_INSERT, openCreds()); 726 GUARD 727 access().Tokend::ClientSession::insertRecord(recordType, 728 attributes, attributesLength, data, record->tokenHandle()); 729 rRecord = record; 730 DONE 731} 732 733void TokenDatabase::modifyRecord(CSSM_DB_RECORDTYPE recordType, Record *rRecord, 734 const CssmDbRecordAttributeData *attributes, mach_msg_type_number_t attributesLength, 735 const CssmData *data, CSSM_DB_MODIFY_MODE modifyMode) 736{ 737 Access access(token()); 738 Record *record = safe_cast<Record *>(rRecord); 739 access.add(*record); 740 TRY 741 validate(CSSM_ACL_AUTHORIZATION_DB_MODIFY, openCreds()); 742 record->validate(CSSM_ACL_AUTHORIZATION_DB_MODIFY, openCreds()); 743 GUARD 744 access().Tokend::ClientSession::modifyRecord(recordType, 745 record->tokenHandle(), attributes, attributesLength, data, modifyMode); 746 DONE 747} 748 749void TokenDatabase::deleteRecord(Database::Record *rRecord) 750{ 751 Access access(token(), *this); 752 Record *record = safe_cast<Record *>(rRecord); 753 access.add(*record); 754 TRY 755 validate(CSSM_ACL_AUTHORIZATION_DB_DELETE, openCreds()); 756 record->validate(CSSM_ACL_AUTHORIZATION_DB_DELETE, openCreds()); 757 GUARD 758 access().Tokend::ClientSession::deleteRecord(record->tokenHandle()); 759 DONE 760} 761 762 763// 764// Record/Search object handling 765// 766TokenDatabase::Search::~Search() 767{ 768 if (mHandle) 769 try { 770 database().token().tokend().Tokend::ClientSession::releaseSearch(mHandle); 771 } catch (...) { 772 secdebug("tokendb", "%p release search handle %u threw (ignored)", 773 this, mHandle); 774 } 775} 776 777TokenDatabase::Record::~Record() 778{ 779 if (mHandle) 780 try { 781 database().token().tokend().Tokend::ClientSession::releaseRecord(mHandle); 782 } catch (...) { 783 secdebug("tokendb", "%p release record handle %u threw (ignored)", 784 this, mHandle); 785 } 786} 787 788 789// 790// TokenAcl personality of Record 791// 792AclKind TokenDatabase::Record::aclKind() const 793{ 794 return objectAcl; 795} 796 797Token &TokenDatabase::Record::token() 798{ 799 return safer_cast<TokenDatabase &>(database()).token(); 800} 801 802GenericHandle TokenDatabase::Record::tokenHandle() const 803{ 804 return Handler::tokenHandle(); 805} 806 807 808// 809// Local utility classes 810// 811void TokenDatabase::InputKey::setup(Key *key) 812{ 813 if (TokenKey *myKey = dynamic_cast<TokenKey *>(key)) { 814 // one of ours 815 mKeyHandle = myKey->tokenHandle(); 816 mKeyPtr = NULL; 817 } else if (LocalKey *hisKey = dynamic_cast<LocalKey *>(key)) { 818 // a local key - turn into raw form 819 CssmClient::WrapKey wrap(Server::csp(), CSSM_ALGID_NONE); 820 wrap(hisKey->cssmKey(), mKey); 821 mKeyHandle = noKey; 822 mKeyPtr = &mKey; 823 } else { 824 // no key at all 825 mKeyHandle = noKey; 826 mKeyPtr = NULL; 827 } 828} 829 830 831TokenDatabase::InputKey::~InputKey() 832{ 833 if (mKeyPtr) { 834 //@@@ Server::csp().freeKey(mKey) ?? 835 Server::csp()->allocator().free(mKey.keyData()); 836 } 837} 838