#include <Security/AuthorizationWalkers.h>
#include "server.h"
#include "ucsp.h"
#include "session.h"
#include "xdatabase.h"
#include <mach/mach_error.h>
#define UCSP_ARGS mach_port_t servicePort, mach_port_t replyPort, security_token_t securityToken, \
CSSM_RETURN *rcode
#define CONTEXT_ARGS Context context, Pointer contextBase, Context::Attr *attributes, mach_msg_type_number_t attrCount
#define BEGIN_IPCN *rcode = CSSM_OK; try {
#define BEGIN_IPC BEGIN_IPCN Connection &connection = Server::connection(replyPort);
#define END_IPC(base) END_IPCN(base) Server::requestComplete(); return KERN_SUCCESS;
#define END_IPCN(base) } \
catch (const CssmCommonError &err) { *rcode = err.cssmError(CSSM_ ## base ## _BASE_ERROR); } \
catch (const std::bad_alloc &) { *rcode = CssmError::merge(CSSM_ERRCODE_MEMORY_ERROR, CSSM_ ## base ## _BASE_ERROR); } \
catch (Connection *conn) { *rcode = 0; } \
catch (...) { *rcode = CssmError::merge(CSSM_ERRCODE_INTERNAL_ERROR, CSSM_ ## base ## _BASE_ERROR); }
#define DATA_IN(base) void *base, mach_msg_type_number_t base##Length
#define DATA_OUT(base) void **base, mach_msg_type_number_t *base##Length
#define DATA(base) CssmData(base, base##Length)
#define COPY_IN(type,name) type *name, mach_msg_type_number_t name##Length, type *name##Base
#define COPY_OUT(type,name) \
type **name, mach_msg_type_number_t *name##Length, type **name##Base
using LowLevelMemoryUtilities::increment;
using LowLevelMemoryUtilities::difference;
class OutputData : public CssmData {
public:
OutputData(void **outP, mach_msg_type_number_t *outLength)
: mData(*outP), mLength(*outLength) { }
~OutputData()
{ mData = data(); mLength = length(); Server::releaseWhenDone(mData); }
void operator = (const CssmData &source)
{ CssmData::operator = (source); }
private:
void * &mData;
mach_msg_type_number_t &mLength;
};
class CheckingReconstituteWalker {
public:
CheckingReconstituteWalker(void *ptr, void *base, size_t size)
: mBase(base), mLimit(increment(base, size)), mOffset(difference(ptr, base)) { }
template <class T>
void operator () (T * &addr, size_t size = sizeof(T))
{
if (addr) {
if (addr < mBase || increment(addr, size) > mLimit)
CssmError::throwMe(CSSM_ERRCODE_INVALID_POINTER);
addr = increment<T>(addr, mOffset);
}
}
static const bool needsRelinking = true;
static const bool needsSize = false;
private:
void *mBase; void *mLimit; off_t mOffset; };
template <class T>
void relocate(T *obj, T *base, size_t size)
{
if (obj) {
if (base == NULL) CssmError::throwMe(CSSM_ERRCODE_INVALID_POINTER);
CheckingReconstituteWalker w(obj, base, size);
walk(w, base);
}
}
kern_return_t ucsp_server_setup(UCSP_ARGS, mach_port_t taskPort, const char *identity)
{
BEGIN_IPCN
Server::active().setupConnection(servicePort, replyPort, taskPort, securityToken, identity);
END_IPCN(CSSM)
return KERN_SUCCESS;
}
kern_return_t ucsp_server_setupNew(UCSP_ARGS, mach_port_t taskPort, const char *identity,
mach_port_t *newServicePort)
{
BEGIN_IPCN
Session *session = new DynamicSession(TaskPort(taskPort).bootstrap());
Server::active().setupConnection(session->servicePort(), replyPort,
taskPort, securityToken, identity);
*newServicePort = session->servicePort();
END_IPCN(CSSM)
return KERN_SUCCESS;
}
kern_return_t ucsp_server_teardown(UCSP_ARGS)
{
BEGIN_IPCN
Server::active().endConnection(replyPort);
END_IPCN(CSSM)
return KERN_SUCCESS;
}
kern_return_t ucsp_server_createDb(UCSP_ARGS, DbHandle *db,
COPY_IN(DLDbFlatIdentifier, ident),
COPY_IN(AccessCredentials, cred), COPY_IN(AclEntryPrototype, owner),
DBParameters params)
{
BEGIN_IPC
relocate(cred, credBase, credLength);
relocate(owner, ownerBase, ownerLength);
relocate(ident, identBase, identLength);
*db = (new Database(*ident, params, connection.process, cred, owner))->handle();
END_IPC(DL)
}
kern_return_t ucsp_server_decodeDb(UCSP_ARGS, DbHandle *db,
COPY_IN(DLDbFlatIdentifier, ident), COPY_IN(AccessCredentials, cred), DATA_IN(blob))
{
BEGIN_IPC
relocate(cred, credBase, credLength);
relocate(ident, identBase, identLength);
*db = (new Database(*ident, DATA(blob).interpretedAs<DbBlob>(),
connection.process, cred))->handle();
END_IPC(DL)
}
kern_return_t ucsp_server_encodeDb(UCSP_ARGS, DbHandle db, DATA_OUT(blob))
{
BEGIN_IPC
DbBlob *dbBlob = Server::database(db).encode(); *blob = dbBlob;
*blobLength = dbBlob->length();
END_IPC(DL)
}
kern_return_t ucsp_server_releaseDb(UCSP_ARGS, DbHandle db)
{
BEGIN_IPC
delete &Server::database(db);
END_IPC(DL)
}
kern_return_t ucsp_server_authenticateDb(UCSP_ARGS, DbHandle db,
COPY_IN(AccessCredentials, cred))
{
BEGIN_IPC
relocate(cred, credBase, credLength);
Server::database(db).authenticate(cred);
END_IPC(DL)
}
kern_return_t ucsp_server_setDbParameters(UCSP_ARGS, DbHandle db, DBParameters params)
{
BEGIN_IPC
Server::database(db).setParameters(params);
END_IPC(DL)
}
kern_return_t ucsp_server_getDbParameters(UCSP_ARGS, DbHandle db, DBParameters *params)
{
BEGIN_IPC
Server::database(db).getParameters(*params);
END_IPC(DL)
}
kern_return_t ucsp_server_changePassphrase(UCSP_ARGS, DbHandle db,
COPY_IN(AccessCredentials, cred))
{
BEGIN_IPC
relocate(cred, credBase, credLength);
Server::database(db).changePassphrase(cred);
END_IPC(DL)
}
kern_return_t ucsp_server_lockDb(UCSP_ARGS, DbHandle db)
{
BEGIN_IPC
Server::database(db).lock();
END_IPC(DL)
}
kern_return_t ucsp_server_unlockDb(UCSP_ARGS, DbHandle db)
{
BEGIN_IPC
Server::database(db).unlock();
END_IPC(DL)
}
kern_return_t ucsp_server_unlockDbWithPassphrase(UCSP_ARGS, DbHandle db, DATA_IN(passphrase))
{
BEGIN_IPC
Server::database(db).unlock(DATA(passphrase));
END_IPC(DL)
}
kern_return_t ucsp_server_isLocked(UCSP_ARGS, DbHandle db, boolean_t *locked)
{
BEGIN_IPC
*locked = Server::database(db).isLocked();
END_IPC(DL)
}
kern_return_t ucsp_server_encodeKey(UCSP_ARGS, KeyHandle keyh, DATA_OUT(blob),
boolean_t wantUid, DATA_OUT(uid))
{
BEGIN_IPC
Key &key = Server::key(keyh);
KeyBlob *keyBlob = key.blob(); *blob = keyBlob;
*blobLength = keyBlob->length();
if (wantUid) {
*uid = &key.uid();
*uidLength = sizeof(KeyUID);
} else {
*uidLength = 0; }
END_IPC(CSP)
}
kern_return_t ucsp_server_decodeKey(UCSP_ARGS, KeyHandle *keyh, CssmKey::Header *header,
DbHandle db, DATA_IN(blob))
{
BEGIN_IPC
Key &key = *new Key(Server::database(db), DATA(blob).interpretedAs<KeyBlob>());
key.returnKey(*keyh, *header);
END_IPC(CSP)
}
kern_return_t ucsp_server_releaseKey(UCSP_ARGS, KeyHandle key)
{
BEGIN_IPC
connection.releaseKey(key);
END_IPC(CSP)
}
kern_return_t ucsp_server_queryKeySizeInBits(UCSP_ARGS, KeyHandle key, CSSM_KEY_SIZE *length)
{
BEGIN_IPC
*length = connection.queryKeySize(findHandle<Key>(key));
END_IPC(CSP)
}
kern_return_t ucsp_server_getOutputSize(UCSP_ARGS, CONTEXT_ARGS, KeyHandle key,
uint32 inputSize, boolean_t encrypt, uint32 *outputSize)
{
BEGIN_IPC
context.postIPC(contextBase, attributes);
*outputSize = connection.getOutputSize(context, findHandle<Key>(key), inputSize, encrypt);
END_IPC(CSP)
}
kern_return_t ucsp_server_generateRandom(UCSP_ARGS, uint32 bytes, DATA_OUT(data))
{
BEGIN_IPC
CssmAllocator &allocator = CssmAllocator::standard(CssmAllocator::sensitive);
void *buffer = allocator.malloc(bytes);
Server::active().random(buffer, bytes);
*data = buffer;
*dataLength = bytes;
Server::releaseWhenDone(allocator, buffer);
END_IPC(CSP)
}
kern_return_t ucsp_server_generateSignature(UCSP_ARGS, CONTEXT_ARGS, KeyHandle key,
CSSM_ALGORITHMS signOnlyAlgorithm, DATA_IN(data), DATA_OUT(signature))
{
BEGIN_IPC
context.postIPC(contextBase, attributes);
OutputData sigData(signature, signatureLength);
connection.generateSignature(context, findHandle<Key>(key), signOnlyAlgorithm,
DATA(data), sigData);
END_IPC(CSP)
}
kern_return_t ucsp_server_verifySignature(UCSP_ARGS, CONTEXT_ARGS, KeyHandle key,
CSSM_ALGORITHMS verifyOnlyAlgorithm, DATA_IN(data), DATA_IN(signature))
{
BEGIN_IPC
context.postIPC(contextBase, attributes);
connection.verifySignature(context, findHandle<Key>(key), verifyOnlyAlgorithm,
DATA(data), DATA(signature));
END_IPC(CSP)
}
kern_return_t ucsp_server_generateMac(UCSP_ARGS, CONTEXT_ARGS, KeyHandle key,
DATA_IN(data), DATA_OUT(mac))
{
BEGIN_IPC
context.postIPC(contextBase, attributes);
OutputData macData(mac, macLength);
connection.generateMac(context, findHandle<Key>(key),
DATA(data), macData);
END_IPC(CSP)
}
kern_return_t ucsp_server_verifyMac(UCSP_ARGS, CONTEXT_ARGS, KeyHandle key,
DATA_IN(data), DATA_IN(mac))
{
BEGIN_IPC
context.postIPC(contextBase, attributes);
connection.verifyMac(context, findHandle<Key>(key),
DATA(data), DATA(mac));
END_IPC(CSP)
}
kern_return_t ucsp_server_encrypt(UCSP_ARGS, CONTEXT_ARGS, KeyHandle key,
DATA_IN(clear), DATA_OUT(cipher))
{
BEGIN_IPC
context.postIPC(contextBase, attributes);
OutputData cipherOut(cipher, cipherLength);
connection.encrypt(context, findHandle<Key>(key),
DATA(clear), cipherOut);
END_IPC(CSP)
}
kern_return_t ucsp_server_decrypt(UCSP_ARGS, CONTEXT_ARGS, KeyHandle key,
DATA_IN(cipher), DATA_OUT(clear))
{
BEGIN_IPC
context.postIPC(contextBase, attributes);
OutputData clearOut(clear, clearLength);
connection.decrypt(context, findHandle<Key>(key),
DATA(cipher), clearOut);
END_IPC(CSP)
}
kern_return_t ucsp_server_generateKey(UCSP_ARGS, DbHandle db, CONTEXT_ARGS,
COPY_IN(AccessCredentials, cred), COPY_IN(AclEntryPrototype, owner),
uint32 usage, uint32 attrs, KeyHandle *newKey, CssmKey::Header *newHeader)
{
BEGIN_IPC
context.postIPC(contextBase, attributes);
relocate(cred, credBase, credLength);
relocate(owner, ownerBase, ownerLength);
Key *key;
connection.generateKey(Server::optionalDatabase(db),
context, cred, owner, usage, attrs, key);
key->returnKey(*newKey, *newHeader);
END_IPC(CSP)
}
kern_return_t ucsp_server_generateKeyPair(UCSP_ARGS, DbHandle db, CONTEXT_ARGS,
COPY_IN(AccessCredentials, cred), COPY_IN(AclEntryPrototype, owner),
uint32 pubUsage, uint32 pubAttrs, uint32 privUsage, uint32 privAttrs,
KeyHandle *pubKey, CssmKey::Header *pubHeader, KeyHandle *privKey, CssmKey::Header *privHeader)
{
BEGIN_IPC
context.postIPC(contextBase, attributes);
relocate(cred, credBase, credLength);
relocate(owner, ownerBase, ownerLength);
Key *pub, *priv;
connection.generateKey(Server::optionalDatabase(db),
context, cred, owner,
pubUsage, pubAttrs, privUsage, privAttrs, pub, priv);
pub->returnKey(*pubKey, *pubHeader);
priv->returnKey(*privKey, *privHeader);
END_IPC(CSP)
}
kern_return_t ucsp_server_deriveKey(UCSP_ARGS, DbHandle db, CONTEXT_ARGS, KeyHandle key,
COPY_IN(AccessCredentials, cred), COPY_IN(AclEntryPrototype, owner),
COPY_IN(void, paramInputData), DATA_OUT(paramOutput),
uint32 usage, uint32 attrs, KeyHandle *newKey, CssmKey::Header *newHeader)
{
BEGIN_IPC
context.postIPC(contextBase, attributes);
relocate(cred, credBase, credLength);
relocate(owner, ownerBase, ownerLength);
CssmData param;
switch (context.algorithm()) {
case CSSM_ALGID_PKCS5_PBKDF2:
relocate((CSSM_PKCS5_PBKDF2_PARAMS *)paramInputData,
(CSSM_PKCS5_PBKDF2_PARAMS *)paramInputDataBase,
paramInputDataLength);
param = CssmData(paramInputData, sizeof(CSSM_PKCS5_PBKDF2_PARAMS));
break;
default:
param = CssmData(paramInputData, paramInputDataLength);
break;
}
Key &theKey = connection.deriveKey(Server::optionalDatabase(db),
context, Server::optionalKey(key), cred, owner, ¶m, usage, attrs);
theKey.returnKey(*newKey, *newHeader);
if (param.length()) {
if (!param) CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
if (paramInputDataLength) param = CssmAutoData(Server::csp().allocator(), param).release();
OutputData(paramOutput, paramOutputLength) = param; }
END_IPC(CSP)
}
kern_return_t ucsp_server_wrapKey(UCSP_ARGS, CONTEXT_ARGS, KeyHandle key,
COPY_IN(AccessCredentials, cred), KeyHandle keyToBeWrapped,
DATA_IN(descriptiveData), CssmKey *wrappedKey, DATA_OUT(keyData))
{
BEGIN_IPC
context.postIPC(contextBase, attributes);
relocate(cred, credBase, credLength);
connection.wrapKey(context, Server::optionalKey(key),
Server::key(keyToBeWrapped), cred, DATA(descriptiveData), *wrappedKey);
*keyData = wrappedKey->data();
*keyDataLength = wrappedKey->length();
Server::releaseWhenDone(*keyData);
END_IPC(CSP)
}
kern_return_t ucsp_server_unwrapKey(UCSP_ARGS, DbHandle db, CONTEXT_ARGS, KeyHandle key,
COPY_IN(AccessCredentials, cred), COPY_IN(AclEntryPrototype, owner),
KeyHandle publicKey, CssmKey wrappedKey, DATA_IN(wrappedKeyData),
uint32 usage, uint32 attr, DATA_OUT(descriptiveData),
KeyHandle *newKey, CssmKey::Header *newHeader)
{
BEGIN_IPC
context.postIPC(contextBase, attributes);
wrappedKey.KeyData = DATA(wrappedKeyData);
relocate(cred, credBase, credLength);
relocate(owner, ownerBase, ownerLength);
CssmData descriptiveDatas;
Key &theKey = connection.unwrapKey(Server::optionalDatabase(db),
context, Server::optionalKey(key), cred, owner, usage, attr, wrappedKey,
Server::optionalKey(publicKey), &descriptiveDatas);
theKey.returnKey(*newKey, *newHeader);
*descriptiveData = descriptiveDatas.data();
*descriptiveDataLength = descriptiveDatas.length();
Server::releaseWhenDone(*descriptiveData);
END_IPC(CSP)
}
kern_return_t ucsp_server_getOwner(UCSP_ARGS, AclKind kind, KeyHandle key,
COPY_OUT(AclOwnerPrototype, ownerOut))
{
BEGIN_IPC
AclOwnerPrototype owner;
Server::aclBearer(kind, key).cssmGetOwner(owner); Copier<AclOwnerPrototype> owners(&owner, CssmAllocator::standard()); { ChunkFreeWalker free; walk(free, owner); } *ownerOut = *ownerOutBase = owners;
*ownerOutLength = owners.length();
Server::releaseWhenDone(owners.keep()); END_IPC(CSP)
}
kern_return_t ucsp_server_setOwner(UCSP_ARGS, AclKind kind, KeyHandle key,
COPY_IN(AccessCredentials, cred), COPY_IN(AclOwnerPrototype, owner))
{
BEGIN_IPC
relocate(cred, credBase, credLength);
relocate(owner, ownerBase, ownerLength);
Server::aclBearer(kind, key).cssmChangeOwner(*owner, cred);
END_IPC(CSP)
}
kern_return_t ucsp_server_getAcl(UCSP_ARGS, AclKind kind, KeyHandle key,
boolean_t haveTag, const char *tag,
uint32 *countp, COPY_OUT(AclEntryInfo, acls))
{
BEGIN_IPC
uint32 count;
AclEntryInfo *aclList;
Server::aclBearer(kind, key).cssmGetAcl(haveTag ? tag : NULL, count, aclList);
*countp = count;
Copier<AclEntryInfo> aclsOut(AclEntryInfo::overlay(aclList), count);
{ ChunkFreeWalker free;
for (uint32 n = 0; n < count; n++)
walk(free, aclList[n]);
}
*acls = *aclsBase = aclsOut;
*aclsLength = aclsOut.length();
Server::releaseWhenDone(aclsOut.keep());
END_IPC(CSP)
}
kern_return_t ucsp_server_changeAcl(UCSP_ARGS, AclKind kind, KeyHandle key,
COPY_IN(AccessCredentials, cred), CSSM_ACL_EDIT_MODE mode, CSSM_ACL_HANDLE handle,
COPY_IN(AclEntryInput, acl))
{
BEGIN_IPC
relocate(cred, credBase, credLength);
relocate(acl, aclBase, aclLength);
Server::aclBearer(kind, key).cssmChangeAcl(AclEdit(mode, handle, acl), cred);
END_IPC(CSP)
}
kern_return_t ucsp_server_authorizationCreate(UCSP_ARGS,
COPY_IN(AuthorizationItemSet, rights),
uint32 flags,
COPY_IN(AuthorizationItemSet, environment),
AuthorizationBlob *authorization)
{
BEGIN_IPC
relocate(rights, rightsBase, rightsLength);
relocate(environment, environmentBase, environmentLength);
*rcode = connection.process.session.authCreate(rights, environment,
flags, *authorization);
END_IPC(CSSM)
}
kern_return_t ucsp_server_authorizationRelease(UCSP_ARGS,
AuthorizationBlob authorization, uint32 flags)
{
BEGIN_IPC
connection.process.session.authFree(authorization, flags);
END_IPC(CSSM)
}
kern_return_t ucsp_server_authorizationCopyRights(UCSP_ARGS,
AuthorizationBlob authorization,
COPY_IN(AuthorizationItemSet, rights),
uint32 flags,
COPY_IN(AuthorizationItemSet, environment),
COPY_OUT(AuthorizationItemSet, result))
{
BEGIN_IPC
relocate(rights, rightsBase, rightsLength);
relocate(environment, environmentBase, environmentLength);
Authorization::MutableRightSet grantedRights;
*rcode = connection.process.session.authGetRights(authorization,
rights, environment, flags, grantedRights);
Copier<AuthorizationRights> returnedRights(grantedRights, CssmAllocator::standard());
*result = *resultBase = returnedRights;
*resultLength = returnedRights.length();
Server::releaseWhenDone(returnedRights.keep());
END_IPC(CSSM)
}
kern_return_t ucsp_server_authorizationCopyInfo(UCSP_ARGS,
AuthorizationBlob authorization,
AuthorizationString tag,
COPY_OUT(AuthorizationItemSet, info))
{
BEGIN_IPC
AuthorizationItemSet *result;
*info = *infoBase = NULL;
*infoLength = 0;
*rcode = connection.process.session.authGetInfo(authorization,
tag[0] ? tag : NULL, result); if (*rcode == noErr)
{
*info = *infoBase = result;
*infoLength = size(result);
Server::releaseWhenDone(result);
}
END_IPC(CSSM)
}
kern_return_t ucsp_server_authorizationExternalize(UCSP_ARGS,
AuthorizationBlob authorization, AuthorizationExternalForm *extForm)
{
BEGIN_IPC
*rcode = connection.process.session.authExternalize(authorization, *extForm);
END_IPC(CSSM)
}
kern_return_t ucsp_server_authorizationInternalize(UCSP_ARGS,
AuthorizationExternalForm extForm, AuthorizationBlob *authorization)
{
BEGIN_IPC
*rcode = connection.process.session.authInternalize(extForm, *authorization);
END_IPC(CSSM)
}
kern_return_t ucsp_server_getSessionInfo(UCSP_ARGS,
SecuritySessionId *sessionId, SessionAttributeBits *attrs)
{
BEGIN_IPC
Session &session = Session::find(*sessionId);
*sessionId = session.handle();
*attrs = session.attributes();
END_IPC(CSSM)
}
kern_return_t ucsp_server_setupSession(UCSP_ARGS,
SessionCreationFlags flags, SessionAttributeBits attrs)
{
BEGIN_IPC
Session::setup(flags, attrs);
END_IPC(CSSM)
}
kern_return_t ucsp_server_requestNotification(UCSP_ARGS, mach_port_t receiver, uint32 domain, uint32 events)
{
BEGIN_IPC
connection.process.requestNotifications(receiver, domain, events);
END_IPC(CSSM)
}
kern_return_t ucsp_server_stopNotification(UCSP_ARGS, mach_port_t receiver)
{
BEGIN_IPC
connection.process.stopNotifications(receiver);
END_IPC(CSSM)
}
kern_return_t ucsp_server_postNotification(UCSP_ARGS, uint32 domain, uint32 event, DATA_IN(data))
{
BEGIN_IPC
connection.process.postNotification(domain, event, DATA(data));
END_IPC(CSSM)
}