/ securityd / src / tokendatabase.cpp
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  }