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