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