]> git.saurik.com Git - apple/security.git/blob - securityd/src/tokendatabase.cpp
Security-59306.80.4.tar.gz
[apple/security.git] / securityd / src / 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 }