#include "connection.h"
#include "key.h"
#include "server.h"
#include "session.h"
#include <Security/keyclient.h>
#include <Security/genkey.h>
#include <Security/wrapkey.h>
#include <Security/signclient.h>
#include <Security/macclient.h>
#include <Security/cryptoclient.h>
Connection::Connection(Process &proc, Port rPort)
: process(proc), mClientPort(rPort), state(idle), agentWait(NULL),
aclUpdateTrigger(NULL)
{
mClientPort.modRefs(MACH_PORT_RIGHT_SEND, +1);
debug("SS", "New connection %p for process %d clientport=%d",
this, process.pid(), int(rPort));
}
Connection::~Connection()
{
debug("SS", "Connection %p destroyed", this);
assert(!agentWait);
}
void Connection::terminate()
{
assert(state == idle);
mClientPort.modRefs(MACH_PORT_RIGHT_SEND, -1); assert(mClientPort.getRefs(MACH_PORT_RIGHT_SEND) == 1); debug("SS", "Connection %p terminated", this);
}
bool Connection::abort(bool keepReplyPort)
{
StLock<Mutex> _(lock);
if (!keepReplyPort)
mClientPort.destroy(); switch (state) {
case idle:
debug("SS", "Connection %p aborted", this);
return true; case busy:
state = dying; if (agentWait)
agentWait->cancel();
debug("SS", "Connection %p abort deferred (busy)", this);
return false; default:
assert(false); }
}
void Connection::beginWork()
{
switch (state) {
case idle:
state = busy;
process.beginConnection(*this);
break;
case busy:
debug("SS", "Attempt to re-enter connection %p(port %d)", this, mClientPort.port());
CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); default:
assert(false);
}
}
void Connection::checkWork()
{
StLock<Mutex> _(lock);
switch (state) {
case busy:
return;
case dying:
agentWait = NULL; throw this;
default:
assert(false);
}
}
bool Connection::endWork()
{
switch (state) {
case busy:
if (aclUpdateTrigger) {
if (--aclUpdateTriggerCount == 0) {
aclUpdateTrigger = NULL;
debug("kcacl", "acl update trigger expires");
} else
debug("kcacl", "acl update trigger armed for %d calls",
aclUpdateTriggerCount);
}
state = idle;
process.endConnection(*this);
return false;
case dying:
debug("SS", "Connection %p abort resuming", this);
if (process.endConnection(*this))
delete &process;
return true;
default:
assert(false);
}
}
void Connection::releaseKey(Key::Handle key)
{
delete &Server::key(key);
}
CSSM_KEY_SIZE Connection::queryKeySize(Key &key)
{
CssmClient::Key theKey(Server::csp(), key);
return theKey.sizeInBits();
}
void Connection::generateSignature(const Context &context, Key &key,
CSSM_ALGORITHMS signOnlyAlgorithm, const CssmData &data, CssmData &signature)
{
context.replace(CSSM_ATTRIBUTE_KEY, (CSSM_KEY &)key);
key.validate(CSSM_ACL_AUTHORIZATION_SIGN, context);
CssmClient::Sign signer(Server::csp(), context.algorithm(), signOnlyAlgorithm);
signer.override(context);
signer.sign(data, signature);
}
void Connection::verifySignature(const Context &context, Key &key,
CSSM_ALGORITHMS verifyOnlyAlgorithm, const CssmData &data, const CssmData &signature)
{
context.replace(CSSM_ATTRIBUTE_KEY, (CSSM_KEY &)key);
CssmClient::Verify verifier(Server::csp(), context.algorithm(), verifyOnlyAlgorithm);
verifier.override(context);
verifier.verify(data, signature);
}
void Connection::generateMac(const Context &context, Key &key,
const CssmData &data, CssmData &mac)
{
context.replace(CSSM_ATTRIBUTE_KEY, (CSSM_KEY &)key);
key.validate(CSSM_ACL_AUTHORIZATION_MAC, context);
CssmClient::GenerateMac signer(Server::csp(), context.algorithm());
signer.override(context);
signer.sign(data, mac);
}
void Connection::verifyMac(const Context &context, Key &key,
const CssmData &data, const CssmData &mac)
{
context.replace(CSSM_ATTRIBUTE_KEY, (CSSM_KEY &)key);
key.validate(CSSM_ACL_AUTHORIZATION_MAC, context);
CssmClient::VerifyMac verifier(Server::csp(), context.algorithm());
verifier.override(context);
verifier.verify(data, mac);
}
void Connection::encrypt(const Context &context, Key &key,
const CssmData &clear, CssmData &cipher)
{
context.replace(CSSM_ATTRIBUTE_KEY, (CSSM_KEY &)key);
key.validate(CSSM_ACL_AUTHORIZATION_ENCRYPT, context);
CssmClient::Encrypt cryptor(Server::csp(), context.algorithm());
cryptor.override(context);
CssmData remData;
size_t totalLength = cryptor.encrypt(clear, cipher, remData);
if (remData)
CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
cipher.length(totalLength);
}
void Connection::decrypt(const Context &context, Key &key,
const CssmData &cipher, CssmData &clear)
{
context.replace(CSSM_ATTRIBUTE_KEY, (CSSM_KEY &)key);
key.validate(CSSM_ACL_AUTHORIZATION_DECRYPT, context);
CssmClient::Decrypt cryptor(Server::csp(), context.algorithm());
cryptor.override(context);
CssmData remData;
size_t totalLength = cryptor.decrypt(cipher, clear, remData);
if (remData)
CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
clear.length(totalLength);
}
void Connection::generateKey(Database *db, const Context &context,
const AccessCredentials *cred, const AclEntryPrototype *owner,
uint32 usage, uint32 attrs, Key * &newKey)
{
CssmClient::GenerateKey generate(Server::csp(), context.algorithm());
generate.override(context);
CssmKey key;
generate(key, Key::KeySpec(usage, attrs));
newKey = new Key(db, key, attrs & Key::managedAttributes, owner);
}
void Connection::generateKey(Database *db, const Context &context,
const AccessCredentials *cred, const AclEntryPrototype *owner,
uint32 pubUsage, uint32 pubAttrs, uint32 privUsage, uint32 privAttrs,
Key * &publicKey, Key * &privateKey)
{
CssmClient::GenerateKey generate(Server::csp(), context.algorithm());
generate.override(context);
Server::active().longTermActivity();
CssmKey pubKey, privKey;
generate(pubKey, Key::KeySpec(pubUsage, pubAttrs),
privKey, Key::KeySpec(privUsage, privAttrs));
publicKey = new Key(db, pubKey, pubAttrs & Key::managedAttributes, owner);
privateKey = new Key(db, privKey, privAttrs & Key::managedAttributes, owner);
}
Key &Connection::deriveKey(Database *db, const Context &context, Key *baseKey,
const AccessCredentials *cred, const AclEntryPrototype *owner,
CssmData *param, uint32 usage, uint32 attrs)
{
if (baseKey) {
baseKey->validate(CSSM_ACL_AUTHORIZATION_DERIVE, cred);
context.replace(CSSM_ATTRIBUTE_KEY, (CSSM_KEY &)*baseKey);
}
CssmClient::DeriveKey derive(Server::csp(), context.algorithm(), CSSM_ALGID_NONE);
derive.override(context);
CssmKey key;
derive(param, Key::KeySpec(usage, attrs), key);
return *new Key(db, key, attrs & Key::managedAttributes, owner);
}
void Connection::wrapKey(const Context &context, Key *key,
Key &keyToBeWrapped, const AccessCredentials *cred,
const CssmData &descriptiveData, CssmKey &wrappedKey)
{
keyToBeWrapped.validate(context.algorithm() == CSSM_ALGID_NONE ?
CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR : CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED,
cred);
if (key)
context.replace(CSSM_ATTRIBUTE_KEY, (CSSM_KEY &)*key);
CssmClient::WrapKey wrap(Server::csp(), context.algorithm());
wrap.override(context);
wrap.cred(const_cast<AccessCredentials *>(cred)); wrap(keyToBeWrapped, wrappedKey, &descriptiveData);
}
Key &Connection::unwrapKey(Database *db, const Context &context, Key *key,
const AccessCredentials *cred, const AclEntryPrototype *owner,
uint32 usage, uint32 attrs, const CssmKey wrappedKey,
Key *publicKey, CssmData *descriptiveData)
{
if (key)
context.replace(CSSM_ATTRIBUTE_KEY, (CSSM_KEY &)*key);
CssmClient::UnwrapKey unwrap(Server::csp(), context.algorithm());
unwrap.override(context);
CssmKey unwrappedKey;
unwrap.cred(const_cast<AccessCredentials *>(cred)); if (owner) {
AclEntryInput ownerInput(*owner); unwrap.aclEntry(ownerInput);
}
unwrap(wrappedKey, Key::KeySpec(usage, attrs), unwrappedKey,
descriptiveData, publicKey ? &static_cast<CssmKey &>(*publicKey) : NULL);
return *new Key(db, unwrappedKey, attrs & Key::managedAttributes, owner);
}
uint32 Connection::getOutputSize(const Context &context, Key &key, uint32 inputSize, bool encrypt)
{
context.replace(CSSM_ATTRIBUTE_KEY, (CSSM_KEY &)key);
CssmClient::Digest ctx(Server::csp(), context.algorithm());
ctx.override(context);
return ctx.getOutputSize(inputSize, encrypt);
}