#include <Security/cssmacl.h>
#include <Security/debugging.h>
#include <algorithm>
#include <cstdarg>
#include <endian.h>
using namespace DataWalkers;
ModuleNexus<ObjectAcl::MakerMap> ObjectAcl::makers;
AclSubject::~AclSubject()
{ }
AclValidationEnvironment::~AclValidationEnvironment()
{ }
void AclSubject::exportBlob(Writer::Counter &, Writer::Counter &)
{ }
void AclSubject::exportBlob(Writer &, Writer &)
{ }
void AclSubject::importBlob(Reader &, Reader &)
{ }
AclSubject::Maker::~Maker()
{
}
bool SimpleAclSubject::validate(const AclValidationContext &ctx) const
{
for (uint32 n = 0; n < ctx.count(); n++) {
const TypedList &sample = ctx[n];
if (!sample.isProper())
CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
if (sample.type() == acceptingSamples && validate(ctx, sample))
return true; }
return false;
}
ObjectAcl::ObjectAcl(CssmAllocator &alloc) : allocator(alloc), nextHandle(1)
{
}
ObjectAcl::ObjectAcl(const AclEntryPrototype &proto, CssmAllocator &alloc)
: allocator(alloc), nextHandle(1)
{
cssmSetInitial(proto);
}
ObjectAcl::~ObjectAcl()
{ }
void ObjectAcl::cssmSetInitial(const AclEntryPrototype &proto)
{
owner = OwnerEntry(proto);
entries.insert(EntryMap::value_type(proto.tag(), proto))->second.handle = nextHandle++;
IFDUMPING("acl", debugDump("create/proto"));
}
void ObjectAcl::cssmSetInitial(const AclSubjectPointer &subject)
{
owner = OwnerEntry(subject);
entries.insert(EntryMap::value_type("", subject))->second.handle = nextHandle++;
IFDUMPING("acl", debugDump("create/subject"));
}
ObjectAcl::Entry::~Entry()
{
}
AclValidationContext::~AclValidationContext()
{
}
class BaseValidationContext : public AclValidationContext {
public:
BaseValidationContext(const AccessCredentials *cred,
AclAuthorization auth, AclValidationEnvironment *env)
: AclValidationContext(cred, auth, env) { }
uint32 count() const { return mCred ? mCred->samples().length() : 0; }
const TypedList &sample(uint32 n) const
{ assert(n < count()); return mCred->samples()[n]; }
};
void ObjectAcl::validate(AclAuthorization auth, const AccessCredentials *cred,
AclValidationEnvironment *env)
{
instantiateAcl();
BaseValidationContext ctx(cred, auth, env);
#if defined(ACL_OMNIPOTENT_OWNER)
if (owner.validate(ctx))
return;
#endif //ACL_OMNIPOTENT_OWNER
pair<ConstIterator, ConstIterator> range;
if (getRange(cred->EntryTag, range) == 0) CssmError::throwMe(CSSM_ERRCODE_ACL_ENTRY_TAG_NOT_FOUND);
for (ConstIterator it = range.first; it != range.second; it++) {
const AclEntry &slot = it->second;
if (slot.authorizes(ctx.authorization()) && slot.validate(ctx))
return; }
CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED); }
void ObjectAcl::validateOwner(AclAuthorization authorizationHint,
const AccessCredentials *cred, AclValidationEnvironment *env)
{
instantiateAcl();
BaseValidationContext ctx(cred, authorizationHint, env);
if (owner.validate(ctx))
return;
CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
}
void ObjectAcl::exportBlob(CssmData &publicBlob, CssmData &privateBlob)
{
Writer::Counter pubSize, privSize;
Endian<uint32> entryCount = entries.size();
owner.exportBlob(pubSize, privSize);
pubSize(entryCount);
for (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);
owner.exportBlob(pubWriter, privWriter);
pubWriter(entryCount);
for (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);
owner.importBlob(pubReader, privReader);
Endian<uint32> entryCountIn; pubReader(entryCountIn);
uint32 entryCount = entryCountIn;
entries.erase(begin(), end());
for (uint32 n = 0; n < entryCount; n++) {
AclEntry newEntry;
newEntry.importBlob(pubReader, privReader);
entries.insert(EntryMap::value_type(newEntry.tag, newEntry))->second.handle = nextHandle++;
}
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 char *tag, pair<ConstIterator, ConstIterator> &range) const
{
if (tag && tag[0]) { range = entries.equal_range(tag);
uint32 count = entries.count(tag);
if (count == 0)
CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_ENTRY_TAG);
return count;
} else { range.first = entries.begin();
range.second = entries.end();
return entries.size();
}
}
ObjectAcl::Iterator ObjectAcl::findEntryHandle(CSSM_ACL_HANDLE handle)
{
for (Iterator it = entries.begin(); it != entries.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<ConstIterator, ConstIterator> range;
count = getRange(tag, range);
acls = allocator.alloc<AclEntryInfo>(count);
uint32 n = 0;
for (ConstIterator it = range.first; it != range.second; it++, n++) {
acls[n].EntryHandle = it->second.handle;
it->second.toEntryInfo(acls[n].EntryPublicInfo, allocator);
}
count = n;
}
void ObjectAcl::cssmChangeAcl(const AclEdit &edit,
const AccessCredentials *cred, AclValidationEnvironment *env)
{
IFDUMPING("acl", debugDump("acl-change-from"));
instantiateAcl();
validateOwner(CSSM_ACL_AUTHORIZATION_CHANGE_ACL, cred, env);
switch (edit.EditMode) {
case CSSM_ACL_EDIT_MODE_ADD: {
AclEntry ent(Required(edit.newEntry()).proto()); ent.handle = nextHandle++;
entries.insert(EntryMap::value_type(edit.NewEntry->Prototype.EntryTag, ent));
}
break;
case CSSM_ACL_EDIT_MODE_REPLACE: {
Iterator it = findEntryHandle(edit.OldEntryHandle);
AclEntry ent(Required(edit.newEntry()).proto());
ent.handle = edit.OldEntryHandle;
entries.insert(EntryMap::value_type(edit.NewEntry->Prototype.EntryTag, ent));
entries.erase(it);
}
break;
case CSSM_ACL_EDIT_MODE_DELETE:
entries.erase(findEntryHandle(edit.OldEntryHandle));
break;
default:
CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_EDIT_MODE);
}
changedAcl();
IFDUMPING("acl", debugDump("acl-change-to"));
}
void ObjectAcl::cssmGetOwner(AclOwnerPrototype &outOwner)
{
instantiateAcl();
outOwner.TypedSubject = owner.subject->toList(allocator);
outOwner.Delegate = owner.delegate;
}
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);
owner = newOwner;
changedAcl();
IFDUMPING("acl", debugDump("owner-change-to"));
}
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::validate(const AclValidationContext &ctx) const
{
return subject->validate(ctx); }
ObjectAcl::AclEntry::AclEntry(const AclEntryPrototype &proto) : Entry(proto)
{
tag = proto.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, CssmAllocator &alloc) const
{
info.TypedSubject = subject->toList(alloc);
info.Delegate = delegate;
info.Authorization = 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::validate(const AclValidationContext &ctx) const
{
return subject->validate(ctx);
}
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) : myType(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(entries.size()));
Debug::dump(" OWNER ["); owner.debugDump(); Debug::dump("]\n");
for (ConstIterator 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
}
void AclSubject::debugDump() const
{
#if defined(DEBUGDUMP)
switch (type()) {
case CSSM_ACL_SUBJECT_TYPE_ANY:
Debug::dump("ANY");
break;
default:
Debug::dump("subject type=%d", int(type()));
break;
}
#endif //DEBUGDUMP
}
#if defined(DEBUGDUMP)
void ObjectAcl::Entry::debugDump() const
{
if (AclSubject::Version v = subject->version())
Debug::dump("V=%d ", v);
subject->debugDump();
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++)
Debug::dump(" %ld", *it);
Debug::dump(")");
}
}
#endif //DEBUGDUMP