#include <security_cdsa_utilities/objectacl.h>
#include <security_cdsa_utilities/cssmbridge.h>
#include <security_utilities/endian.h>
#include <security_utilities/debugging.h>
#include <algorithm>
#include <cstdarg>
#include <security_cdsa_utilities/acl_preauth.h> //@@@ impure - will be removed
using namespace DataWalkers;
ModuleNexus<ObjectAcl::MakerMap> ObjectAcl::makers;
ObjectAcl::ObjectAcl(Allocator &alloc) : allocator(alloc), mNextHandle(1)
{
}
ObjectAcl::ObjectAcl(const AclEntryPrototype &proto, Allocator &alloc)
: allocator(alloc), mNextHandle(1)
{
cssmSetInitial(proto);
}
ObjectAcl::~ObjectAcl()
{ }
void ObjectAcl::cssmSetInitial(const AclEntryPrototype &proto)
{
mOwner = OwnerEntry(proto);
add(proto.s_tag(), proto);
IFDUMPING("acl", debugDump("create/proto"));
}
void ObjectAcl::cssmSetInitial(const AclSubjectPointer &subject)
{
mOwner = OwnerEntry(subject);
add("", subject);
IFDUMPING("acl", debugDump("create/subject"));
}
ObjectAcl::Entry::~Entry()
{
}
bool ObjectAcl::validates(AclAuthorization auth, const AccessCredentials *cred,
AclValidationEnvironment *env)
{
BaseValidationContext ctx(cred, auth, env);
return validates(ctx);
}
bool ObjectAcl::validates(AclValidationContext &ctx)
{
instantiateAcl();
IFDUMPING("acleval", Debug::dump("<<WANT(%d)<", ctx.authorization()));
#if defined(ACL_OMNIPOTENT_OWNER)
if (mOwner.validates(ctx))
return;
#endif //ACL_OMNIPOTENT_OWNER
pair<EntryMap::const_iterator, EntryMap::const_iterator> range;
if (getRange(ctx.s_credTag(), range) == 0) {
secinfo("SecAccess", "no tag for cred tag: \"%s\"", ctx.s_credTag().c_str());
CssmError::throwMe(CSSM_ERRCODE_ACL_ENTRY_TAG_NOT_FOUND);
}
for (EntryMap::const_iterator it = range.first; it != range.second; it++) {
const AclEntry &slot = it->second;
IFDUMPING("acleval", (Debug::dump(" EVAL["), slot.debugDump(), Debug::dump("]")));
if (slot.authorizes(ctx.authorization())) {
ctx.init(this, slot.subject);
ctx.entryTag(slot.tag);
if (slot.validates(ctx)) {
IFDUMPING("acleval", Debug::dump(">PASS>>\n"));
return true; }
IFDUMPING("acleval", Debug::dump(" NO"));
}
}
IFDUMPING("acleval", Debug::dump(">FAIL>>\n"));
return false; }
void ObjectAcl::validate(AclAuthorization auth, const AccessCredentials *cred,
AclValidationEnvironment *env)
{
if (!validates(auth, cred, env))
CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
}
void ObjectAcl::validate(AclValidationContext &ctx)
{
if (!validates(ctx))
CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
}
void ObjectAcl::validateOwner(AclAuthorization authorizationHint,
const AccessCredentials *cred, AclValidationEnvironment *env)
{
BaseValidationContext ctx(cred, authorizationHint, env);
validateOwner(ctx);
}
void ObjectAcl::validateOwner(AclValidationContext &ctx)
{
instantiateAcl();
ctx.init(this, mOwner.subject);
if (mOwner.validates(ctx))
return;
CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
}
void ObjectAcl::exportBlob(CssmData &publicBlob, CssmData &privateBlob)
{
instantiateAcl();
Writer::Counter pubSize, privSize;
Endian<uint32> entryCount = (uint32)mEntries.size();
mOwner.exportBlob(pubSize, privSize);
pubSize(entryCount);
for (EntryMap::iterator it = begin(); it != end(); it++)
it->second.exportBlob(pubSize, privSize);
publicBlob = CssmData(allocator.malloc(pubSize), pubSize);
privateBlob = CssmData(allocator.malloc(privSize), privSize);
Writer pubWriter(publicBlob), privWriter(privateBlob);
mOwner.exportBlob(pubWriter, privWriter);
pubWriter(entryCount);
for (EntryMap::iterator it = begin(); it != end(); it++)
it->second.exportBlob(pubWriter, privWriter);
IFDUMPING("acl", debugDump("exported"));
}
void ObjectAcl::importBlob(const void *publicBlob, const void *privateBlob)
{
Reader pubReader(publicBlob), privReader(privateBlob);
mOwner.importBlob(pubReader, privReader);
Endian<uint32> entryCountIn; pubReader(entryCountIn);
uint32 entryCount = entryCountIn;
mEntries.erase(begin(), end());
for (uint32 n = 0; n < entryCount; n++) {
AclEntry newEntry;
newEntry.importBlob(pubReader, privReader);
add(newEntry.tag, newEntry);
}
IFDUMPING("acl", debugDump("imported"));
}
AclSubject *ObjectAcl::importSubject(Reader &pub, Reader &priv)
{
Endian<uint32> typeAndVersion; pub(typeAndVersion);
return make(typeAndVersion, pub, priv);
}
void ObjectAcl::instantiateAcl()
{
}
void ObjectAcl::changedAcl()
{
}
unsigned int ObjectAcl::getRange(const std::string &tag,
pair<EntryMap::const_iterator, EntryMap::const_iterator> &range, bool tolerant ) const
{
if (!tag.empty()) { secinfo("SecAccess", "looking for ACL entries matching tag: \"%s\"", tag.c_str());
range = mEntries.equal_range(tag);
unsigned int count = (unsigned int)mEntries.count(tag);
if (count == 0 && !tolerant)
CssmError::throwMe(CSSM_ERRCODE_ACL_ENTRY_TAG_NOT_FOUND);
return count;
} else { secinfo("SecAccess", "no tag given; looking for all ACL entries");
range.first = mEntries.begin();
range.second = mEntries.end();
return (unsigned int)mEntries.size();
}
}
ObjectAcl::EntryMap::iterator ObjectAcl::findEntryHandle(CSSM_ACL_HANDLE handle)
{
for (EntryMap::iterator it = mEntries.begin(); it != mEntries.end(); it++)
if (it->second.handle == handle)
return it;
CssmError::throwMe(CSSMERR_CSSM_INVALID_HANDLE_USAGE); }
void ObjectAcl::cssmGetAcl(const char *tag, uint32 &count, AclEntryInfo * &acls)
{
instantiateAcl();
pair<EntryMap::const_iterator, EntryMap::const_iterator> range;
count = getRange(tag ? tag : "", range);
acls = allocator.alloc<AclEntryInfo>(count);
uint32 n = 0;
secinfo("SecAccess", "getting the ACL for %p (%d entries) tag: %s", this, count, tag ? tag : "<none>");
for (EntryMap::const_iterator it = range.first; it != range.second; it++, n++) {
acls[n].EntryHandle = it->second.handle;
it->second.toEntryInfo(acls[n].EntryPublicInfo, allocator);
secinfo("SecAccess", "found an entry of type %d", acls[n].EntryPublicInfo.TypedSubject.Head->WordID);
}
count = n;
}
void ObjectAcl::cssmChangeAcl(const AclEdit &edit,
const AccessCredentials *cred, AclValidationEnvironment *env, const char *preserveTag)
{
IFDUMPING("acl", debugDump("acl-change-from"));
instantiateAcl();
validateOwner(CSSM_ACL_AUTHORIZATION_CHANGE_ACL, cred, env);
switch (edit.EditMode) {
case CSSM_ACL_EDIT_MODE_ADD: {
secinfo("SecAccess", "adding ACL for %p (%ld) while preserving: %s", this, edit.handle(), preserveTag);
const AclEntryInput &input = Required(edit.newEntry());
if (preserveTag && input.proto().s_tag() == preserveTag)
MacOSError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
add(input.proto().s_tag(), input.proto());
secinfo("SecAccess", "subject type is %d", input.proto().TypedSubject.Head->WordID);
}
break;
case CSSM_ACL_EDIT_MODE_REPLACE: {
secinfo("SecAccess", "replacing ACL for %p (%ld to %p) while preserving: %s", this, edit.handle(), edit.newEntry(), preserveTag);
EntryMap::iterator it = findEntryHandle(edit.handle());
if (preserveTag && it->second.tag == preserveTag)
MacOSError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
AclEntryPrototype proto2;
it->second.toEntryInfo(proto2, allocator);
secinfo("SecAccess", "subject type was %d", proto2.TypedSubject.Head->WordID);
DataWalkers::chunkFree(proto2, allocator);
AclEntryPrototype proto = Required(edit.newEntry()).proto(); add(proto.s_tag(), proto, edit.handle());
secinfo("SecAccess", "new subject type is %d", proto.TypedSubject.Head->WordID);
mEntries.erase(it);
}
break;
case CSSM_ACL_EDIT_MODE_DELETE: {
secinfo("SecAccess", "deleting ACL for %p (%ld) while preserving: %s", this, edit.handle(), preserveTag);
EntryMap::iterator it = findEntryHandle(edit.handle());
if (preserveTag && it->second.tag == preserveTag)
MacOSError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
AclEntryPrototype proto;
it->second.toEntryInfo(proto, allocator);
secinfo("SecAccess", "subject type was %d", proto.TypedSubject.Head->WordID);
DataWalkers::chunkFree(proto, allocator);
mEntries.erase(it);
break;
}
default:
secinfo("SecAccess", "no idea what this CSSM_ACL_EDIT type is: %d", edit.EditMode);
CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_EDIT_MODE);
}
changedAcl();
IFDUMPING("acl", debugDump("acl-change-to"));
}
void ObjectAcl::cssmGetOwner(AclOwnerPrototype &outOwner)
{
instantiateAcl();
outOwner.TypedSubject = mOwner.subject->toList(allocator);
outOwner.Delegate = mOwner.delegate;
secinfo("SecAccess", "%p: getting the owner ACL: type %d", this, outOwner.TypedSubject.Head->WordID);
}
void ObjectAcl::cssmChangeOwner(const AclOwnerPrototype &newOwner,
const AccessCredentials *cred, AclValidationEnvironment *env)
{
IFDUMPING("acl", debugDump("owner-change-from"));
instantiateAcl();
validateOwner(CSSM_ACL_AUTHORIZATION_CHANGE_OWNER, cred, env);
mOwner = newOwner;
secinfo("SecAccess", "%p: new owner's type is %d", this, newOwner.subject().Head->WordID);
changedAcl();
IFDUMPING("acl", debugDump("owner-change-to"));
}
template <class Input>
void ObjectAcl::owner(const Input &input)
{
IFDUMPING("acl", debugDump("owner-load-old"));
mOwner = OwnerEntry(input);
IFDUMPING("acl", debugDump("owner-load-new"));
}
template void ObjectAcl::owner(const AclOwnerPrototype &);
template void ObjectAcl::owner(const AclSubjectPointer &);
void ObjectAcl::entries(uint32 count, const AclEntryInfo *info)
{
IFDUMPING("acl", debugDump("entries-load-old"));
mEntries.erase(mEntries.begin(), mEntries.end());
for (uint32 n = 0; n < count; n++, info++)
add(info->proto().s_tag(), info->proto());
IFDUMPING("acl", debugDump("entries-load-new"));
}
void ObjectAcl::clear()
{
mOwner = OwnerEntry();
mEntries.erase(mEntries.begin(), mEntries.end());
secinfo("acl", "%p cleared", this);
}
void ObjectAcl::add(const std::string &tag, const AclEntry &newEntry)
{
add(tag, newEntry, mNextHandle++);
}
void ObjectAcl::add(const std::string &tag, AclEntry newEntry, CSSM_ACL_HANDLE handle)
{
newEntry.tag = tag;
if (!newEntry.authorizesAnything) {
for (AclAuthorizationSet::const_iterator it = newEntry.authorizations.begin();
it != newEntry.authorizations.end(); it++)
if (*it >= CSSM_ACL_AUTHORIZATION_PREAUTH_BASE &&
*it < CSSM_ACL_AUTHORIZATION_PREAUTH_END) {
if (newEntry.subject->type() != CSSM_ACL_SUBJECT_TYPE_PREAUTH_SOURCE)
newEntry.subject =
new PreAuthorizationAcls::SourceAclSubject(newEntry.subject);
}
}
mEntries.insert(make_pair(tag, newEntry))->second.handle = handle;
if (handle >= mNextHandle)
mNextHandle = handle + 1; }
void ObjectAcl::Entry::init(const AclSubjectPointer &subject, bool delegate)
{
this->subject = subject;
this->delegate = delegate;
}
void ObjectAcl::Entry::importBlob(Reader &pub, Reader &priv)
{
Endian<uint32> del;
pub(del);
delegate = del;
subject = importSubject(pub, priv);
}
bool ObjectAcl::OwnerEntry::authorizes(AclAuthorization) const
{
return true; }
bool ObjectAcl::OwnerEntry::validates(const AclValidationContext &ctx) const
{
if (AclValidationEnvironment* env = ctx.environment())
if (env->forceSuccess)
return true;
return subject->validates(ctx); }
ObjectAcl::AclEntry::AclEntry(const AclEntryPrototype &proto) : Entry(proto)
{
tag = proto.s_tag();
if (proto.authorization().contains(CSSM_ACL_AUTHORIZATION_ANY))
authorizesAnything = true; else if (proto.authorization().empty())
authorizesAnything = true; else {
authorizesAnything = false;
authorizations = proto.authorization();
}
}
ObjectAcl::AclEntry::AclEntry(const AclSubjectPointer &subject) : Entry(subject)
{
authorizesAnything = true; }
void ObjectAcl::AclEntry::toEntryInfo(CSSM_ACL_ENTRY_PROTOTYPE &info, Allocator &alloc) const
{
info.TypedSubject = subject->toList(alloc);
info.Delegate = delegate;
info.Authorization = authorizesAnything ?
AuthorizationGroup(CSSM_ACL_AUTHORIZATION_ANY, alloc) :
AuthorizationGroup(authorizations, alloc);
assert(tag.length() <= CSSM_MODULE_STRING_SIZE);
memcpy(info.EntryTag, tag.c_str(), tag.length() + 1);
}
bool ObjectAcl::AclEntry::authorizes(AclAuthorization auth) const
{
return authorizesAnything || authorizations.find(auth) != authorizations.end();
}
bool ObjectAcl::AclEntry::validates(const AclValidationContext &ctx) const
{
return subject->validates(ctx);
}
void ObjectAcl::AclEntry::addAuthorization(AclAuthorization auth)
{
authorizations.insert(auth);
authorizesAnything = false;
}
void ObjectAcl::AclEntry::importBlob(Reader &pub, Reader &priv)
{
Entry::importBlob(pub, priv);
const char *s; pub(s); tag = s;
Endian<uint32> tmpAuthorizesAnything;
pub(tmpAuthorizesAnything);
authorizesAnything = tmpAuthorizesAnything;
authorizations.erase(authorizations.begin(), authorizations.end());
if (!authorizesAnything) {
Endian<uint32> countIn; pub(countIn);
uint32 count = countIn;
for (uint32 n = 0; n < count; n++) {
Endian<AclAuthorization> auth; pub(auth);
authorizations.insert(auth);
}
}
}
AclSubject::Maker::Maker(CSSM_ACL_SUBJECT_TYPE type)
: mType(type)
{
ObjectAcl::makers()[type] = this;
}
AclSubject *ObjectAcl::make(const TypedList &list)
{
if (!list.isProper())
CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
return makerFor(list.type()).make(list);
}
AclSubject *ObjectAcl::make(uint32 typeAndVersion, Reader &pub, Reader &priv)
{
return makerFor(typeAndVersion & ~AclSubject::versionMask).make(typeAndVersion >> AclSubject::versionShift, pub, priv);
}
AclSubject::Maker &ObjectAcl::makerFor(CSSM_ACL_SUBJECT_TYPE type)
{
AclSubject::Maker *maker = makers()[type];
if (maker == NULL)
CssmError::throwMe(CSSM_ERRCODE_ACL_SUBJECT_TYPE_NOT_SUPPORTED);
return *maker;
}
void AclSubject::Maker::crack(const CssmList &list, uint32 count, ListElement **array, ...)
{
if (count != list.length() - 1)
CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
if (count > 0) {
va_list args;
va_start(args, array);
ListElement *elem = list.first()->next();
for (uint32 n = 0; n < count; n++, elem = elem->next()) {
CSSM_LIST_ELEMENT_TYPE expectedType = va_arg(args, CSSM_LIST_ELEMENT_TYPE);
if (elem->type() != expectedType)
CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
array[n] = elem;
}
va_end(args);
}
}
CSSM_WORDID_TYPE AclSubject::Maker::getWord(const ListElement &elem,
int min , int max )
{
if (elem.type() != CSSM_LIST_ELEMENT_WORDID)
CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
CSSM_WORDID_TYPE value = elem;
if (value < min || value > max)
CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
return value;
}
void ObjectAcl::debugDump(const char *what) const
{
#if defined(DEBUGDUMP)
if (!what)
what = "Dump";
Debug::dump("%p ACL %s: %d entries\n", this, what, int(mEntries.size()));
Debug::dump(" OWNER ["); mOwner.debugDump(); Debug::dump("]\n");
for (EntryMap::const_iterator it = begin(); it != end(); it++) {
const AclEntry &ent = it->second;
Debug::dump(" (%ld:%s) [", ent.handle, ent.tag.c_str());
ent.debugDump();
Debug::dump("]\n");
}
Debug::dump("%p ACL END\n", this);
#endif //DEBUGDUMP
}
#if defined(DEBUGDUMP)
void ObjectAcl::Entry::debugDump() const
{
if (subject) {
if (AclSubject::Version v = subject->version())
Debug::dump("V=%d ", v);
subject->debugDump();
} else {
Debug::dump("NULL subject");
}
if (delegate)
Debug::dump(" DELEGATE");
}
void ObjectAcl::AclEntry::debugDump() const
{
Entry::debugDump();
if (authorizesAnything) {
Debug::dump(" auth(ALL)");
} else {
Debug::dump(" auth(");
for (AclAuthorizationSet::iterator it = authorizations.begin();
it != authorizations.end(); it++) {
if (*it >= CSSM_ACL_AUTHORIZATION_PREAUTH_BASE
&& *it < CSSM_ACL_AUTHORIZATION_PREAUTH_END)
Debug::dump(" PRE(%d)", *it - CSSM_ACL_AUTHORIZATION_PREAUTH_BASE);
else
Debug::dump(" %d", *it);
}
Debug::dump(")");
}
}
#endif //DEBUGDUMP