2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 // tokendatabase - software database container implementation.
28 #include "tokendatabase.h"
30 #include "tokenaccess.h"
33 #include "localkey.h" // to retrieve local raw keys
34 #include <security_cdsa_client/wrapkey.h>
38 // Construct a TokenDbCommon
40 TokenDbCommon::TokenDbCommon(Session
&ssn
, Token
&tk
, const char *name
)
41 : DbCommon(ssn
), mDbName(name
? name
: ""), mResetLevel(0)
43 secdebug("tokendb", "creating tokendbcommon %p: with token %p", this, &tk
);
47 TokenDbCommon::~TokenDbCommon()
49 secdebug("tokendb", "destroying tokendbcommon %p", this);
50 token().removeCommon(*this); // unregister from Token
53 Token
&TokenDbCommon::token() const
55 return parent
<Token
>();
58 const std::string
&TokenDbCommon::dbName() const
60 return token().printName();
65 // A TokenDbCommon holds per-session adornments for the ACL machine
67 Adornable
&TokenDbCommon::store()
69 StLock
<Mutex
> _(*this);
71 // if this is the first one, hook for lifetime
72 if (mAdornments
.empty()) {
73 session().addReference(*this); // hold and slave to SSN lifetime
74 token().addCommon(*this); // register with Token
77 // return our (now active) adornments
81 void TokenDbCommon::resetAcls()
83 StLock
<Mutex
> _(*this);
84 if (!mAdornments
.empty()) {
85 mAdornments
.clearAdornments(); // clear ACL state
86 session().removeReference(*this); // unhook from SSN
88 token().removeCommon(*this); // unregister from Token
93 // Process (our part of) a "lock all" request.
94 // Smartcard tokens interpret a "lock" as a forced card reset, transmitted
95 // to tokend as an authenticate request.
96 // @@@ Virtual reset for multi-session tokens. Right now, we're using the sledge hammer.
98 void TokenDbCommon::lockProcessing()
100 Access
access(token());
101 access().authenticate(CSSM_DB_ACCESS_RESET
, NULL
);
105 // Construct a TokenDatabase given subservice information.
106 // We are currently ignoring the 'name' argument.
108 TokenDatabase::TokenDatabase(uint32 ssid
, Process
&proc
,
109 const char *name
, const AccessCredentials
*cred
)
112 // locate Token object
113 RefPointer
<Token
> token
= Token::find(ssid
);
115 Session
&session
= process().session();
116 StLock
<Mutex
> _(session
);
117 if (TokenDbCommon
*dbcom
= session
.findFirst
<TokenDbCommon
, uint32
>(&TokenDbCommon::subservice
, ssid
)) {
119 secdebug("tokendb", "open tokendb %p(%ld) at known common %p",
120 this, subservice(), dbcom
);
122 // DbCommon not present; make a new one
123 parent(*new TokenDbCommon(proc
.session(), *token
, name
));
124 secdebug("tokendb", "open tokendb %p(%ld) with new common %p",
125 this, subservice(), &common());
127 mOpenCreds
= copy(cred
, Allocator::standard());
128 proc
.addReference(*this);
131 TokenDatabase::~TokenDatabase()
133 Allocator::standard().free(mOpenCreds
);
138 // Basic Database virtual implementations
140 TokenDbCommon
&TokenDatabase::common() const
142 return parent
<TokenDbCommon
>();
145 TokenDaemon
&TokenDatabase::tokend()
147 return common().token().tokend();
150 const char *TokenDatabase::dbName() const
152 return common().dbName().c_str();
155 bool TokenDatabase::transient() const
157 //@@@ let tokend decide? Are there any secure transient keystores?
162 SecurityServerAcl
&TokenDatabase::acl()
167 bool TokenDatabase::isLocked() const
169 Access
access(token());
170 return access().isLocked();
175 // TokenDatabases implement the dbName-setting function.
176 // This sets the print name of the token, which is persistently
177 // stored in the token cache. So this is a de-facto rename of
178 // the token, at least on this system.
180 void TokenDatabase::dbName(const char *name
)
182 CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED
);
187 // Given a key handle and CssmKey returned from tokend, create a Key representing
188 // it. This takes care of raw returns by turning them into keys of the process's
189 // local transient store.
191 RefPointer
<Key
> TokenDatabase::makeKey(KeyHandle hKey
, const CssmKey
*key
,
192 const AclEntryPrototype
*owner
)
194 switch (key
->blobType()) {
195 case CSSM_KEYBLOB_REFERENCE
:
196 return new TokenKey(*this, hKey
, key
->header());
197 case CSSM_KEYBLOB_RAW
:
198 return process().makeTemporaryKey(*key
, 0, owner
);
200 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
); // bad key return from tokend
202 //@@@ Server::releaseWhenDone(key);
207 // Adjust key attributes for newly created keys
209 static CSSM_KEYATTR_FLAGS
modattrs(CSSM_KEYATTR_FLAGS attrs
)
211 static const CSSM_KEYATTR_FLAGS CSSM_KEYATTR_RETURN_FLAGS
= 0xff000000;
212 switch (attrs
& CSSM_KEYATTR_RETURN_FLAGS
) {
213 case CSSM_KEYATTR_RETURN_REF
:
214 case CSSM_KEYATTR_RETURN_DATA
:
215 break; // as requested
216 case CSSM_KEYATTR_RETURN_NONE
:
217 CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEYATTR_MASK
);
218 case CSSM_KEYATTR_RETURN_DEFAULT
:
219 if (attrs
& CSSM_KEYATTR_PERMANENT
)
220 attrs
|= CSSM_KEYATTR_RETURN_REF
;
222 attrs
|= CSSM_KEYATTR_RETURN_DATA
;
230 // TokenDatabases support remote secret validation by sending a secret
231 // (aka passphrase et al) to tokend for processing.
233 bool TokenDatabase::validateSecret(const AclSubject
*subject
, const AccessCredentials
*cred
)
235 secdebug("tokendb", "%p attempting remote validation", this);
237 Access
access(token());
238 // @@@ Use cached mode
239 access().authenticate(CSSM_DB_ACCESS_READ
, cred
);
240 secdebug("tokendb", "%p remote validation successful", this);
244 secdebug("tokendb", "%p remote validation failed", this);
246 throw; // try not to mask error
254 void TokenDatabase::queryKeySizeInBits(Key
&key
, CssmKeySize
&result
)
256 Access
access(token());
259 access().queryKeySizeInBits(myKey(key
).tokenHandle(), result
);
265 // Signatures and MACs
267 void TokenDatabase::generateSignature(const Context
&context
, Key
&key
,
268 CSSM_ALGORITHMS signOnlyAlgorithm
, const CssmData
&data
, CssmData
&signature
)
270 Access
access(token(), key
);
272 key
.validate(CSSM_ACL_AUTHORIZATION_SIGN
, context
);
274 access().generateSignature(context
, myKey(key
).tokenHandle(), data
, signature
, signOnlyAlgorithm
);
279 void TokenDatabase::verifySignature(const Context
&context
, Key
&key
,
280 CSSM_ALGORITHMS verifyOnlyAlgorithm
, const CssmData
&data
, const CssmData
&signature
)
282 Access
access(token(), key
);
285 access().verifySignature(context
, myKey(key
).tokenHandle(), data
, signature
, verifyOnlyAlgorithm
);
289 void TokenDatabase::generateMac(const Context
&context
, Key
&key
,
290 const CssmData
&data
, CssmData
&mac
)
292 Access
access(token());
294 key
.validate(CSSM_ACL_AUTHORIZATION_MAC
, context
);
296 access().generateMac(context
, myKey(key
).tokenHandle(), data
, mac
);
300 void TokenDatabase::verifyMac(const Context
&context
, Key
&key
,
301 const CssmData
&data
, const CssmData
&mac
)
303 Access
access(token());
305 key
.validate(CSSM_ACL_AUTHORIZATION_MAC
, context
);
307 access().verifyMac(context
, myKey(key
).tokenHandle(), data
, mac
);
313 // Encryption/decryption
315 void TokenDatabase::encrypt(const Context
&context
, Key
&key
,
316 const CssmData
&clear
, CssmData
&cipher
)
318 Access
access(token());
320 key
.validate(CSSM_ACL_AUTHORIZATION_ENCRYPT
, context
);
322 access().encrypt(context
, myKey(key
).tokenHandle(), clear
, cipher
);
327 void TokenDatabase::decrypt(const Context
&context
, Key
&key
,
328 const CssmData
&cipher
, CssmData
&clear
)
330 Access
access(token());
332 key
.validate(CSSM_ACL_AUTHORIZATION_DECRYPT
, context
);
334 access().decrypt(context
, myKey(key
).tokenHandle(), cipher
, clear
);
340 // Key generation and derivation.
341 // Currently, we consider symmetric key generation to be fast, but
342 // asymmetric key generation to be (potentially) slow.
344 void TokenDatabase::generateKey(const Context
&context
,
345 const AccessCredentials
*cred
, const AclEntryPrototype
*owner
,
346 CSSM_KEYUSE usage
, CSSM_KEYATTR_FLAGS attrs
, RefPointer
<Key
> &newKey
)
348 Access
access(token());
353 access().generateKey(context
, cred
, owner
, usage
, modattrs(attrs
), hKey
, result
);
354 newKey
= makeKey(hKey
, result
, owner
);
358 void TokenDatabase::generateKey(const Context
&context
,
359 const AccessCredentials
*cred
, const AclEntryPrototype
*owner
,
360 CSSM_KEYUSE pubUsage
, CSSM_KEYATTR_FLAGS pubAttrs
,
361 CSSM_KEYUSE privUsage
, CSSM_KEYATTR_FLAGS privAttrs
,
362 RefPointer
<Key
> &publicKey
, RefPointer
<Key
> &privateKey
)
364 Access
access(token());
367 KeyHandle hPrivate
, hPublic
;
368 CssmKey
*privKey
, *pubKey
;
369 access().generateKey(context
, cred
, owner
,
370 pubUsage
, modattrs(pubAttrs
), privUsage
, modattrs(privAttrs
),
371 hPublic
, pubKey
, hPrivate
, privKey
);
372 publicKey
= makeKey(hPublic
, pubKey
, owner
);
373 privateKey
= makeKey(hPrivate
, privKey
, owner
);
379 // Key wrapping and unwrapping.
380 // Note that the key argument (the key in the context) is optional because of the special
381 // case of "cleartext" (null algorithm) wrapping for import/export.
383 void TokenDatabase::wrapKey(const Context
&context
, const AccessCredentials
*cred
,
384 Key
*wrappingKey
, Key
&subjectKey
,
385 const CssmData
&descriptiveData
, CssmKey
&wrappedKey
)
387 Access
access(token());
388 InputKey
cWrappingKey(wrappingKey
);
389 InputKey
cSubjectKey(subjectKey
);
391 subjectKey
.validate(context
.algorithm() == CSSM_ALGID_NONE
?
392 CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR
: CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED
,
395 wrappingKey
->validate(CSSM_ACL_AUTHORIZATION_ENCRYPT
, context
);
397 CssmKey
*rWrappedKey
;
398 access().wrapKey(context
, cred
,
399 cWrappingKey
, cWrappingKey
, cSubjectKey
, cSubjectKey
,
400 descriptiveData
, rWrappedKey
);
401 wrappedKey
= *rWrappedKey
;
402 //@@@ ownership of wrappedKey.keyData() ??
406 void TokenDatabase::unwrapKey(const Context
&context
,
407 const AccessCredentials
*cred
, const AclEntryPrototype
*owner
,
408 Key
*wrappingKey
, Key
*publicKey
, CSSM_KEYUSE usage
, CSSM_KEYATTR_FLAGS attrs
,
409 const CssmKey wrappedKey
, RefPointer
<Key
> &unwrappedKey
, CssmData
&descriptiveData
)
411 Access
access(token());
412 InputKey
cWrappingKey(wrappingKey
);
413 InputKey
cPublicKey(publicKey
);
416 wrappingKey
->validate(CSSM_ACL_AUTHORIZATION_DECRYPT
, context
);
417 // we are not checking access on the public key, if any
421 access().unwrapKey(context
, cred
, owner
,
422 cWrappingKey
, cWrappingKey
, cPublicKey
, cPublicKey
,
423 wrappedKey
, usage
, modattrs(attrs
), descriptiveData
, hKey
, result
);
424 unwrappedKey
= makeKey(hKey
, result
, owner
);
432 void TokenDatabase::deriveKey(const Context
&context
, Key
*sourceKey
,
433 const AccessCredentials
*cred
, const AclEntryPrototype
*owner
,
434 CssmData
*param
, CSSM_KEYUSE usage
, CSSM_KEYATTR_FLAGS attrs
, RefPointer
<Key
> &derivedKey
)
436 Access
access(token());
437 InputKey
cSourceKey(sourceKey
);
440 sourceKey
->validate(CSSM_ACL_AUTHORIZATION_DERIVE
, cred
);
444 CssmData params
= param
? *param
: CssmData();
445 access().deriveKey(noDb
, context
,
446 cSourceKey
, cSourceKey
,
447 usage
, modattrs(attrs
), params
, cred
, owner
,
451 //@@@ leak? what's the rule here?
453 derivedKey
= makeKey(hKey
, result
, owner
);
459 // Miscellaneous CSSM functions
461 void TokenDatabase::getOutputSize(const Context
&context
, Key
&key
,
462 uint32 inputSize
, bool encrypt
, uint32
&result
)
464 Access
access(token());
467 access().getOutputSize(context
, myKey(key
).tokenHandle(), inputSize
, encrypt
, result
);
473 // (Re-)Authenticate the database.
474 // We use dbAuthenticate as the catch-all "do something about authentication" call.
476 void TokenDatabase::authenticate(CSSM_DB_ACCESS_TYPE mode
, const AccessCredentials
*cred
)
478 Access
access(token());
481 if (mode
!= CSSM_DB_ACCESS_RESET
&& cred
) {
482 secdebug("tokendb", "%p authenticate calling validate", this);
484 if (sscanf(cred
->EntryTag
, "PIN%d", &pin
) == 1)
485 return validate(CSSM_ACL_AUTHORIZATION_PREAUTH(pin
), cred
);
488 access().authenticate(mode
, cred
);
490 case CSSM_DB_ACCESS_RESET
:
491 // this mode is known to trigger "lockdown" (i.e. reset)
492 common().resetAcls();
496 // no idea what that did to the token;
497 // But let's remember the new creds for our own sake.
498 AccessCredentials
*newCred
= copy(cred
, Allocator::standard());
499 Allocator::standard().free(mOpenCreds
);
500 mOpenCreds
= newCred
;
508 // Data access interface.
510 // Note that the attribute vectors are passed between our two IPC interfaces
511 // as relocated but contiguous memory blocks (to avoid an extra copy). This means
512 // you can read them at will, but can't change them in transit unless you're
513 // willing to repack them right here.
515 void TokenDatabase::findFirst(const CssmQuery
&query
,
516 CssmDbRecordAttributeData
*inAttributes
, mach_msg_type_number_t inAttributesLength
,
517 CssmData
*data
, RefPointer
<Key
> &key
,
518 RefPointer
<Database::Search
> &rSearch
, RefPointer
<Database::Record
> &rRecord
,
519 CssmDbRecordAttributeData
* &outAttributes
, mach_msg_type_number_t
&outAttributesLength
)
521 Access
access(token());
522 RefPointer
<Search
> search
= new Search(*this);
523 RefPointer
<Record
> record
= new Record(*this);
525 KeyHandle hKey
= noKey
;
526 validate(CSSM_ACL_AUTHORIZATION_DB_READ
, openCreds());
528 record
->tokenHandle() = access().Tokend::ClientSession::findFirst(query
,
529 inAttributes
, inAttributesLength
, search
->tokenHandle(), NULL
, hKey
,
530 outAttributes
, outAttributesLength
);
531 if (!record
->tokenHandle()) { // no match (but no other error)
532 rRecord
= NULL
; // return null record
537 record
->validate(CSSM_ACL_AUTHORIZATION_DB_READ
, openCreds());
538 CssmDbRecordAttributeData
*noAttributes
;
539 mach_msg_type_number_t noAttributesLength
;
540 access().Tokend::ClientSession::findRecordHandle(record
->tokenHandle(),
541 NULL
, 0, data
, hKey
, noAttributes
, noAttributesLength
);
542 if (hKey
) { // tokend returned a key reference & data
543 CssmKey
&keyForm
= *data
->interpretedAs
<CssmKey
>(CSSMERR_CSP_INVALID_KEY
);
544 key
= new TokenKey(*this, hKey
, keyForm
.header());
547 rSearch
= search
->commit();
548 rRecord
= record
->commit();
552 void TokenDatabase::findNext(Database::Search
*rSearch
,
553 CssmDbRecordAttributeData
*inAttributes
, mach_msg_type_number_t inAttributesLength
,
554 CssmData
*data
, RefPointer
<Key
> &key
, RefPointer
<Database::Record
> &rRecord
,
555 CssmDbRecordAttributeData
* &outAttributes
, mach_msg_type_number_t
&outAttributesLength
)
557 Access
access(token());
558 RefPointer
<Record
> record
= new Record(*this);
559 Search
*search
= safe_cast
<Search
*>(rSearch
);
561 KeyHandle hKey
= noKey
;
562 validate(CSSM_ACL_AUTHORIZATION_DB_READ
, openCreds());
564 record
->tokenHandle() = access().Tokend::ClientSession::findNext(
565 search
->tokenHandle(), inAttributes
, inAttributesLength
,
566 NULL
, hKey
, outAttributes
, outAttributesLength
);
567 if (!record
->tokenHandle()) { // no more matches
568 releaseSearch(*search
); // release search handle (consumed by EOD)
569 rRecord
= NULL
; // return null record
574 record
->validate(CSSM_ACL_AUTHORIZATION_DB_READ
, openCreds());
575 CssmDbRecordAttributeData
*noAttributes
;
576 mach_msg_type_number_t noAttributesLength
;
577 access().Tokend::ClientSession::findRecordHandle(record
->tokenHandle(),
578 NULL
, 0, data
, hKey
, noAttributes
, noAttributesLength
);
579 if (hKey
) { // tokend returned a key reference & data
580 CssmKey
&keyForm
= *data
->interpretedAs
<CssmKey
>(CSSMERR_CSP_INVALID_KEY
);
581 key
= new TokenKey(*this, hKey
, keyForm
.header());
584 rRecord
= record
->commit();
588 void TokenDatabase::findRecordHandle(Database::Record
*rRecord
,
589 CssmDbRecordAttributeData
*inAttributes
, mach_msg_type_number_t inAttributesLength
,
590 CssmData
*data
, RefPointer
<Key
> &key
,
591 CssmDbRecordAttributeData
* &outAttributes
, mach_msg_type_number_t
&outAttributesLength
)
593 Access
access(token());
594 Record
*record
= safe_cast
<Record
*>(rRecord
);
597 KeyHandle hKey
= noKey
;
598 validate(CSSM_ACL_AUTHORIZATION_DB_READ
, openCreds());
600 record
->validate(CSSM_ACL_AUTHORIZATION_DB_READ
, openCreds());
602 access().Tokend::ClientSession::findRecordHandle(record
->tokenHandle(),
603 inAttributes
, inAttributesLength
, data
, hKey
, outAttributes
, outAttributesLength
);
605 if (hKey
!= noKey
&& data
) { // tokend returned a key reference & data
606 CssmKey
&keyForm
= *data
->interpretedAs
<CssmKey
>(CSSMERR_CSP_INVALID_KEY
);
607 key
= new TokenKey(*this, hKey
, keyForm
.header());
612 void TokenDatabase::insertRecord(CSSM_DB_RECORDTYPE recordType
,
613 const CssmDbRecordAttributeData
*attributes
, mach_msg_type_number_t attributesLength
,
614 const CssmData
&data
, RefPointer
<Database::Record
> &rRecord
)
616 Access
access(token());
617 RefPointer
<Record
> record
= new Record(*this);
620 validate(CSSM_ACL_AUTHORIZATION_DB_INSERT
, openCreds());
622 access().Tokend::ClientSession::insertRecord(recordType
,
623 attributes
, attributesLength
, data
, record
->tokenHandle());
628 void TokenDatabase::modifyRecord(CSSM_DB_RECORDTYPE recordType
, Record
*rRecord
,
629 const CssmDbRecordAttributeData
*attributes
, mach_msg_type_number_t attributesLength
,
630 const CssmData
*data
, CSSM_DB_MODIFY_MODE modifyMode
)
632 Access
access(token());
633 Record
*record
= safe_cast
<Record
*>(rRecord
);
636 validate(CSSM_ACL_AUTHORIZATION_DB_MODIFY
, openCreds());
637 record
->validate(CSSM_ACL_AUTHORIZATION_DB_MODIFY
, openCreds());
639 access().Tokend::ClientSession::modifyRecord(recordType
,
640 record
->tokenHandle(), attributes
, attributesLength
, data
, modifyMode
);
644 void TokenDatabase::deleteRecord(Database::Record
*rRecord
)
646 Access
access(token(), *this);
647 Record
*record
= safe_cast
<Record
*>(rRecord
);
650 validate(CSSM_ACL_AUTHORIZATION_DB_DELETE
, openCreds());
651 record
->validate(CSSM_ACL_AUTHORIZATION_DB_DELETE
, openCreds());
653 access().Tokend::ClientSession::deleteRecord(record
->tokenHandle());
659 // Record/Search object handling
661 TokenDatabase::Search::~Search()
665 database().token().tokend().Tokend::ClientSession::releaseSearch(mHandle
);
667 secdebug("tokendb", "%p release search handle %ld threw (ignored)",
672 TokenDatabase::Record::~Record()
676 database().token().tokend().Tokend::ClientSession::releaseRecord(mHandle
);
678 secdebug("tokendb", "%p release record handle %ld threw (ignored)",
685 // TokenAcl personality of Record
687 AclKind
TokenDatabase::Record::aclKind() const
692 Token
&TokenDatabase::Record::token()
694 return safer_cast
<TokenDatabase
&>(database()).token();
697 GenericHandle
TokenDatabase::Record::tokenHandle() const
699 return Handler::tokenHandle();
704 // Local utility classes
706 void TokenDatabase::InputKey::setup(Key
*key
)
708 if (TokenKey
*myKey
= dynamic_cast<TokenKey
*>(key
)) {
710 mKeyHandle
= myKey
->tokenHandle();
712 } else if (LocalKey
*hisKey
= dynamic_cast<LocalKey
*>(key
)) {
713 // a local key - turn into raw form
714 CssmClient::WrapKey
wrap(Server::csp(), CSSM_ALGID_NONE
);
715 wrap(hisKey
->cssmKey(), mKey
);
726 TokenDatabase::InputKey::~InputKey()
729 //@@@ Server::csp().freeKey(mKey) ??
730 Server::csp()->allocator().free(mKey
.keyData());