+++ /dev/null
-/*
- * Copyright (c) 2000-2004,2006,2011-2014 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-
-//
-// objectacl - core implementation of an ACL-bearing object
-//
-#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;
-
-
-//
-// The static map of available ACL subject makers.
-// These are the kinds of ACL subjects we can deal with.
-//
-ModuleNexus<ObjectAcl::MakerMap> ObjectAcl::makers;
-
-
-//
-// Create an ObjectAcl
-//
-ObjectAcl::ObjectAcl(Allocator &alloc) : allocator(alloc), mNextHandle(1)
-{
-}
-
-ObjectAcl::ObjectAcl(const AclEntryPrototype &proto, Allocator &alloc)
- : allocator(alloc), mNextHandle(1)
-{
- cssmSetInitial(proto);
-}
-
-ObjectAcl::~ObjectAcl()
-{ }
-
-
-//
-// Set an "initial ACL" from a CSSM-style initial ACL argument.
-// This will replace the owner, as well as replace the entire ACL
-// with a single-item slot, as per CSSM specification.
-//
-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()
-{
-}
-
-
-//
-// ObjectAcl::validate validates an access authorization claim.
-// 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)
-{
- BaseValidationContext ctx(cred, auth, env);
- return validates(ctx);
-}
-
-bool ObjectAcl::validates(AclValidationContext &ctx)
-{
- // make sure we are ready to go
- instantiateAcl();
-
- IFDUMPING("acleval", Debug::dump("<<WANT(%d)<", ctx.authorization()));
-
- //@@@ should pre-screen based on requested auth, maybe?
-
-#if defined(ACL_OMNIPOTENT_OWNER)
- // try owner (owner can do anything)
- if (mOwner.validate(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
- 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;
- 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.validate(ctx)) {
- IFDUMPING("acleval", Debug::dump(">PASS>>\n"));
- return true; // passed
- }
- IFDUMPING("acleval", Debug::dump(" NO"));
- }
- }
- IFDUMPING("acleval", Debug::dump(">FAIL>>\n"));
- return false; // no joy
-}
-
-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.validate(ctx))
- return;
- CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
-}
-
-
-//
-// Export an ObjectAcl to two memory blobs: public and private data separated.
-// This is a standard two-pass size+copy operation.
-//
-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"));
-}
-
-
-//
-// Import an ObjectAcl's contents from two memory blobs representing public and
-// private contents, respectively. These blobs must have been generated by the
-// export method.
-// Prior contents (if any) are deleted and replaced.
-//
-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"));
-}
-
-
-//
-// Import/export helpers for subjects.
-// This is exported to (subject implementation) callers to maintain consistency
-// in binary format handling.
-//
-AclSubject *ObjectAcl::importSubject(Reader &pub, Reader &priv)
-{
- Endian<uint32> typeAndVersion; pub(typeAndVersion);
- return make(typeAndVersion, pub, priv);
-}
-
-
-//
-// Setup/update hooks
-//
-void ObjectAcl::instantiateAcl()
-{
- // nothing by default
-}
-
-void ObjectAcl::changedAcl()
-{
- // nothing by default
-}
-
-
-//
-// ACL utility methods
-//
-unsigned int ObjectAcl::getRange(const std::string &tag,
- pair<EntryMap::const_iterator, EntryMap::const_iterator> &range) const
-{
- if (!tag.empty()) { // tag restriction in effect
- range = mEntries.equal_range(tag);
- unsigned int count = (unsigned int)mEntries.count(tag);
- if (count == 0)
- CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_ENTRY_TAG);
- return count;
- } else { // try all tags
- 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); //%%% imprecise error code
-}
-
-
-//
-// CSSM style ACL access and modification functions.
-//
-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;
- 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);
- }
- count = n;
-}
-
-void ObjectAcl::cssmChangeAcl(const AclEdit &edit,
- const AccessCredentials *cred, AclValidationEnvironment *env)
-{
- IFDUMPING("acl", debugDump("acl-change-from"));
-
- // make sure we're ready to go
- instantiateAcl();
-
- // 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: {
- const AclEntryInput &input = Required(edit.newEntry());
- add(input.proto().s_tag(), input.proto());
- }
- break;
- case CSSM_ACL_EDIT_MODE_REPLACE: {
- // keep the handle, and try for some modicum of atomicity
- EntryMap::iterator it = findEntryHandle(edit.handle());
- AclEntryPrototype proto = Required(edit.newEntry()).proto(); // (bypassing callbacks)
- add(proto.s_tag(), proto, edit.handle());
- mEntries.erase(it);
- }
- break;
- case CSSM_ACL_EDIT_MODE_DELETE:
- mEntries.erase(findEntryHandle(edit.OldEntryHandle));
- break;
- default:
- CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_EDIT_MODE);
- }
-
- // notify change
- changedAcl();
-
- IFDUMPING("acl", debugDump("acl-change-to"));
-}
-
-void ObjectAcl::cssmGetOwner(AclOwnerPrototype &outOwner)
-{
- instantiateAcl();
- outOwner.TypedSubject = mOwner.subject->toList(allocator);
- outOwner.Delegate = mOwner.delegate;
-}
-
-void ObjectAcl::cssmChangeOwner(const AclOwnerPrototype &newOwner,
- const AccessCredentials *cred, AclValidationEnvironment *env)
-{
- IFDUMPING("acl", debugDump("owner-change-from"));
-
- instantiateAcl();
-
- // only the owner entry can match
- validateOwner(CSSM_ACL_AUTHORIZATION_CHANGE_OWNER, cred, env);
-
- // okay, replace it
- mOwner = newOwner;
-
- changedAcl();
-
- IFDUMPING("acl", debugDump("owner-change-to"));
-}
-
-
-//
-// Load a set of ACL entries from an AclEntryInfo array.
-// This completely replaces the ACL's entries.
-// Note that we will adopt the handles in the infos, so they better be proper
-// (unique, nonzero).
-//
-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"));
-}
-
-
-//
-// Clear out the ACL and return it to un-initialized state
-//
-void ObjectAcl::clear()
-{
- mOwner = OwnerEntry();
- mEntries.erase(mEntries.begin(), mEntries.end());
- secdebug("acl", "%p cleared", this);
-}
-
-
-//
-// Common gate to add an ACL entry
-//
-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)
-{
- //@@@ This should use a hook-registry mechanism. But for now, we are explicit:
- 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) {
- // preauthorization right - special handling
- 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; // don't reuse this handle (in this ACL)
-}
-
-
-//
-// Common features of ACL entries/owners
-//
-void ObjectAcl::Entry::init(const AclSubjectPointer &subject, bool delegate)
-{
- this->subject = subject;
- this->delegate = delegate;
-}
-
-void ObjectAcl::Entry::importBlob(Reader &pub, Reader &priv)
-{
- // the delegation flag is 4 bytes for historic reasons
- Endian<uint32> del;
- pub(del);
- delegate = del;
-
- subject = importSubject(pub, priv);
-}
-
-
-//
-// An OwnerEntry is a restricted EntryPrototype for use as the ACL owner.
-//
-bool ObjectAcl::OwnerEntry::authorizes(AclAuthorization) const
-{
- return true; // owner can do anything
-}
-
-bool ObjectAcl::OwnerEntry::validate(const AclValidationContext &ctx) const
-{
- return subject->validate(ctx); // simple subject match - no strings attached
-}
-
-
-//
-// An AclEntry has some extra goodies
-//
-ObjectAcl::AclEntry::AclEntry(const AclEntryPrototype &proto) : Entry(proto)
-{
- tag = proto.s_tag();
- if (proto.authorization().contains(CSSM_ACL_AUTHORIZATION_ANY))
- authorizesAnything = true; // anything else wouldn't add anything
- else if (proto.authorization().empty())
- authorizesAnything = true; // not in standard, but common sense
- else {
- authorizesAnything = false;
- authorizations = proto.authorization();
- }
- //@@@ not setting time range
- // handle = not set here. Set by caller when the AclEntry is created.
-}
-
-ObjectAcl::AclEntry::AclEntry(const AclSubjectPointer &subject) : Entry(subject)
-{
- authorizesAnything = true; // by default, everything
- //@@@ not setting time range
-}
-
-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);
- //@@@ info.TimeRange =
- 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
-{
- //@@@ not checking time ranges
- return subject->validate(ctx);
-}
-
-void ObjectAcl::AclEntry::importBlob(Reader &pub, Reader &priv)
-{
- Entry::importBlob(pub, priv);
- const char *s; pub(s); tag = s;
-
- // authorizesAnything is on disk as a 4-byte flag
- 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);
- }
- }
- //@@@ import time range
-}
-
-
-//
-// Subject factory and makers
-//
-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)
-{
- // this type is encoded as (version << 24) | type
- 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;
-}
-
-
-//
-// Parsing helper for subject makers.
-// Note that count/array exclude the first element of list, which is the subject type wordid.
-//
-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 /*= 0*/, int max /*= 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;
-}
-
-
-//
-// Debug dumping support.
-// Leave the ObjectAcl::debugDump method in (stubbed out)
-// to keep the virtual table layout stable, and to allow
-// proper linking in weird mix-and-match scenarios.
-//
-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