#include <security_cdsa_utilities/cssmbridge.h>
#include <security_utilities/endian.h>
#include <security_utilities/debugging.h>
+#include <security_utilities/threading.h>
#include <algorithm>
#include <cstdarg>
// These are the kinds of ACL subjects we can deal with.
//
ModuleNexus<ObjectAcl::MakerMap> ObjectAcl::makers;
+NormalMutex ObjectAcl::makersMutex;
//
// Returns normally if 'auth' is granted to the bearer of 'cred'.
// Otherwise, throws a suitable (ACL-related) CssmError exception.
//
-class BaseValidationContext : public AclValidationContext {
-public:
- BaseValidationContext(const AccessCredentials *cred,
- AclAuthorization auth, AclValidationEnvironment *env)
- : AclValidationContext(cred, auth, env) { }
-
- uint32 count() const { return cred() ? cred()->samples().length() : 0; }
- uint32 size() const { return count(); }
- const TypedList &sample(uint32 n) const
- { assert(n < count()); return cred()->samples()[n]; }
-
- void matched(const TypedList *) const { } // ignore match info
-};
-
-
bool ObjectAcl::validates(AclAuthorization auth, const AccessCredentials *cred,
AclValidationEnvironment *env)
{
#if defined(ACL_OMNIPOTENT_OWNER)
// try owner (owner can do anything)
- if (mOwner.validate(ctx))
+ if (mOwner.validates(ctx))
return;
#endif //ACL_OMNIPOTENT_OWNER
// try applicable ACLs
pair<EntryMap::const_iterator, EntryMap::const_iterator> range;
- if (getRange(ctx.s_credTag(), range) == 0) // no such tag
+ if (getRange(ctx.s_credTag(), range) == 0) {
+ // no such tag
+ secinfo("SecAccess", "no tag for cred tag: \"%s\"", ctx.s_credTag().c_str());
CssmError::throwMe(CSSM_ERRCODE_ACL_ENTRY_TAG_NOT_FOUND);
+ }
// try each entry in turn
for (EntryMap::const_iterator it = range.first; it != range.second; it++) {
const AclEntry &slot = it->second;
if (slot.authorizes(ctx.authorization())) {
ctx.init(this, slot.subject);
ctx.entryTag(slot.tag);
- if (slot.validate(ctx)) {
+ if (slot.validates(ctx)) {
IFDUMPING("acleval", Debug::dump(">PASS>>\n"));
return true; // passed
}
instantiateAcl();
ctx.init(this, mOwner.subject);
- if (mOwner.validate(ctx))
+ if (mOwner.validates(ctx))
return;
CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
}
// ACL utility methods
//
unsigned int ObjectAcl::getRange(const std::string &tag,
- pair<EntryMap::const_iterator, EntryMap::const_iterator> &range) const
+ pair<EntryMap::const_iterator, EntryMap::const_iterator> &range, bool tolerant /* = false */) const
{
if (!tag.empty()) { // tag restriction in effect
+ 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)
- CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_ENTRY_TAG);
+ if (count == 0 && !tolerant)
+ CssmError::throwMe(CSSM_ERRCODE_ACL_ENTRY_TAG_NOT_FOUND);
return count;
} else { // try all tags
+ secinfo("SecAccess", "no tag given; looking for all ACL entries");
range.first = mEntries.begin();
range.second = mEntries.end();
return (unsigned int)mEntries.size();
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 AccessCredentials *cred, AclValidationEnvironment *env, const char *preserveTag)
{
IFDUMPING("acl", debugDump("acl-change-from"));
// validate access credentials
validateOwner(CSSM_ACL_AUTHORIZATION_CHANGE_ACL, cred, env);
-
+
// what is Thy wish, effendi?
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);
// keep the handle, and try for some modicum of atomicity
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(); // (bypassing callbacks)
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:
- mEntries.erase(findEntryHandle(edit.OldEntryHandle));
+ 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);
}
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,
// okay, replace it
mOwner = newOwner;
+
+ secinfo("SecAccess", "%p: new owner's type is %d", this, newOwner.subject().Head->WordID);
changedAcl();
{
mOwner = OwnerEntry();
mEntries.erase(mEntries.begin(), mEntries.end());
- secdebug("acl", "%p cleared", this);
+ secinfo("acl", "%p cleared", this);
}
void ObjectAcl::add(const std::string &tag, AclEntry newEntry, CSSM_ACL_HANDLE handle)
{
+ newEntry.tag = tag;
//@@@ This should use a hook-registry mechanism. But for now, we are explicit:
if (!newEntry.authorizesAnything) {
for (AclAuthorizationSet::const_iterator it = newEntry.authorizations.begin();
return true; // owner can do anything
}
-bool ObjectAcl::OwnerEntry::validate(const AclValidationContext &ctx) const
+bool ObjectAcl::OwnerEntry::validates(const AclValidationContext &ctx) const
{
- return subject->validate(ctx); // simple subject match - no strings attached
+ if (AclValidationEnvironment* env = ctx.environment())
+ if (env->forceSuccess)
+ return true;
+ return subject->validates(ctx); // simple subject match - no strings attached
}
return authorizesAnything || authorizations.find(auth) != authorizations.end();
}
-bool ObjectAcl::AclEntry::validate(const AclValidationContext &ctx) const
+bool ObjectAcl::AclEntry::validates(const AclValidationContext &ctx) const
{
//@@@ not checking time ranges
- return subject->validate(ctx);
+ 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);
AclSubject::Maker::Maker(CSSM_ACL_SUBJECT_TYPE type)
: mType(type)
{
+ StLock<Mutex> _(ObjectAcl::makersMutex);
ObjectAcl::makers()[type] = this;
}
AclSubject::Maker &ObjectAcl::makerFor(CSSM_ACL_SUBJECT_TYPE type)
{
+ StLock<Mutex> _(ObjectAcl::makersMutex);
AclSubject::Maker *maker = makers()[type];
if (maker == NULL)
CssmError::throwMe(CSSM_ERRCODE_ACL_SUBJECT_TYPE_NOT_SUPPORTED);