]> git.saurik.com Git - apple/securityd.git/blob - src/tokendatabase.cpp
securityd-55199.2.tar.gz
[apple/securityd.git] / src / tokendatabase.cpp
1 /*
2 * Copyright (c) 2000-2007 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), mResetLevel(0)
42 {
43 secdebug("tokendb", "creating tokendbcommon %p: with token %p", this, &tk);
44 parent(tk);
45 }
46
47 TokenDbCommon::~TokenDbCommon()
48 {
49 secdebug("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 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
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 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
222 bool 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
233 bool 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 //
282 void 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 //
293 RefPointer<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 //
311 static 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 //
335 bool 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 //
356 void 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 //
369 void 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
381 void 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
391 void 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
402 void 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 //
417 void 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
429 void 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 //
446 void 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
460 void 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 //
485 void 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
508 void 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 //
534 void 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 //
563 void 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 //
578 void 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 //
620 void 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
657 void 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
693 void 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
717 void 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
733 void 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
749 void 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 //
766 TokenDatabase::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
777 TokenDatabase::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 //
792 AclKind TokenDatabase::Record::aclKind() const
793 {
794 return objectAcl;
795 }
796
797 Token &TokenDatabase::Record::token()
798 {
799 return safer_cast<TokenDatabase &>(database()).token();
800 }
801
802 GenericHandle TokenDatabase::Record::tokenHandle() const
803 {
804 return Handler::tokenHandle();
805 }
806
807
808 //
809 // Local utility classes
810 //
811 void 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
831 TokenDatabase::InputKey::~InputKey()
832 {
833 if (mKeyPtr) {
834 //@@@ Server::csp().freeKey(mKey) ??
835 Server::csp()->allocator().free(mKey.keyData());
836 }
837 }