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//
40TokenDbCommon::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
47TokenDbCommon::~TokenDbCommon()
48{
49	secdebug("tokendb", "destroying tokendbcommon %p", this);
50	token().removeCommon(*this);		// unregister from Token
51}
52
53Token &TokenDbCommon::token() const
54{
55	return parent<Token>();
56}
57
58std::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//
67Adornable &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
82void 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//
97void 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//
110void 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//
120TokenDatabase::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
143TokenDatabase::~TokenDatabase()
144{
145	Allocator::standard().free(mOpenCreds);
146}
147
148
149//
150// Basic Database virtual implementations
151//
152TokenDbCommon &TokenDatabase::common() const
153{
154	return parent<TokenDbCommon>();
155}
156
157TokenDaemon &TokenDatabase::tokend()
158{
159	return common().token().tokend();
160}
161
162const 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
169bool 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//
179SecurityServerAcl &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//
189void 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
222bool 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
233bool 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//
282void 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//
293RefPointer<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//
311static 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//
335bool 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//
356void 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//
369void 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
381void 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
391void 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
402void 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//
417void 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
429void 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//
446void 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
460void 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//
485void 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
508void 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//
534void 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//
563void 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//
578void 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//
620void 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
657void 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
693void 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
717void 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
733void 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
749void 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//
766TokenDatabase::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
777TokenDatabase::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//
792AclKind TokenDatabase::Record::aclKind() const
793{
794	return objectAcl;
795}
796
797Token &TokenDatabase::Record::token()
798{
799	return safer_cast<TokenDatabase &>(database()).token();
800}
801
802GenericHandle TokenDatabase::Record::tokenHandle() const
803{
804	return Handler::tokenHandle();
805}
806
807
808//
809// Local utility classes
810//
811void 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
831TokenDatabase::InputKey::~InputKey()
832{
833	if (mKeyPtr) {
834		//@@@ Server::csp().freeKey(mKey) ??
835		Server::csp()->allocator().free(mKey.keyData());
836	}
837}
838