X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/Security/libsecurity_cdsa_utilities/lib/objectacl.cpp?ds=inline diff --git a/Security/libsecurity_cdsa_utilities/lib/objectacl.cpp b/Security/libsecurity_cdsa_utilities/lib/objectacl.cpp deleted file mode 100644 index c89cb273..00000000 --- a/Security/libsecurity_cdsa_utilities/lib/objectacl.cpp +++ /dev/null @@ -1,662 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include - -#include //@@@ 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::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("< 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 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 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 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 &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 range; - count = getRange(tag ? tag : "", range); - acls = allocator.alloc(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 -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 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 tmpAuthorizesAnything; - pub(tmpAuthorizesAnything); - authorizesAnything = tmpAuthorizesAnything; - - authorizations.erase(authorizations.begin(), authorizations.end()); - if (!authorizesAnything) { - Endian countIn; pub(countIn); - uint32 count = countIn; - - for (uint32 n = 0; n < count; n++) { - Endian 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