]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_cdsa_utilities/lib/objectacl.cpp
Security-58286.20.16.tar.gz
[apple/security.git] / OSX / libsecurity_cdsa_utilities / lib / objectacl.cpp
index c89cb273121b504f9430513d8ea1fc3fb07061c4..80989e2d2895bd01ef49536870653ce64969a2fc 100644 (file)
@@ -29,6 +29,7 @@
 #include <security_cdsa_utilities/cssmbridge.h>
 #include <security_utilities/endian.h>
 #include <security_utilities/debugging.h>
 #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>
 
 #include <algorithm>
 #include <cstdarg>
 
@@ -42,6 +43,7 @@ using namespace DataWalkers;
 // These are the kinds of ACL subjects we can deal with.
 //
 ModuleNexus<ObjectAcl::MakerMap> ObjectAcl::makers;
 // These are the kinds of ACL subjects we can deal with.
 //
 ModuleNexus<ObjectAcl::MakerMap> ObjectAcl::makers;
+NormalMutex ObjectAcl::makersMutex;
 
 
 //
 
 
 //
@@ -90,21 +92,6 @@ ObjectAcl::Entry::~Entry()
 // Returns normally if 'auth' is granted to the bearer of 'cred'.
 // Otherwise, throws a suitable (ACL-related) CssmError exception.
 //
 // 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)
 {
 bool ObjectAcl::validates(AclAuthorization auth, const AccessCredentials *cred,
     AclValidationEnvironment *env)
 {
@@ -123,14 +110,17 @@ bool ObjectAcl::validates(AclValidationContext &ctx)
 
 #if defined(ACL_OMNIPOTENT_OWNER)
     // try owner (owner can do anything)
 
 #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;
         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);
         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;
     // try each entry in turn
     for (EntryMap::const_iterator it = range.first; it != range.second; it++) {
         const AclEntry &slot = it->second;
@@ -138,7 +128,7 @@ bool ObjectAcl::validates(AclValidationContext &ctx)
         if (slot.authorizes(ctx.authorization())) {
                        ctx.init(this, slot.subject);
                        ctx.entryTag(slot.tag);
         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
                        }
                                IFDUMPING("acleval", Debug::dump(">PASS>>\n"));
                                return true;            // passed
                        }
@@ -175,7 +165,7 @@ void ObjectAcl::validateOwner(AclValidationContext &ctx)
     instantiateAcl();
     
     ctx.init(this, mOwner.subject);
     instantiateAcl();
     
     ctx.init(this, mOwner.subject);
-    if (mOwner.validate(ctx))
+    if (mOwner.validates(ctx))
         return;
     CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
 }
         return;
     CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
 }
@@ -258,15 +248,17 @@ void ObjectAcl::changedAcl()
 // ACL utility methods
 //
 unsigned int ObjectAcl::getRange(const std::string &tag,
 // 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
 {
     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);
         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
         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();
         range.first = mEntries.begin();
         range.second = mEntries.end();
         return (unsigned int)mEntries.size();
@@ -292,15 +284,19 @@ void ObjectAcl::cssmGetAcl(const char *tag, uint32 &count, AclEntryInfo * &acls)
     count = getRange(tag ? tag : "", range);
     acls = allocator.alloc<AclEntryInfo>(count);
     uint32 n = 0;
     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);
     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,
     }
     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"));
 
 {
        IFDUMPING("acl", debugDump("acl-change-from"));
 
@@ -309,26 +305,51 @@ void ObjectAcl::cssmChangeAcl(const AclEdit &edit,
 
     // validate access credentials
     validateOwner(CSSM_ACL_AUTHORIZATION_CHANGE_ACL, cred, env);
 
     // 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: {
     // 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());
                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());
                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: {
                }
         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());
                // 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());
                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;
                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;
         break;
+               }
     default:
     default:
+        secinfo("SecAccess", "no idea what this CSSM_ACL_EDIT type is: %d", edit.EditMode);
         CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_EDIT_MODE);
     }
        
         CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_EDIT_MODE);
     }
        
@@ -343,6 +364,8 @@ void ObjectAcl::cssmGetOwner(AclOwnerPrototype &outOwner)
        instantiateAcl();
     outOwner.TypedSubject = mOwner.subject->toList(allocator);
     outOwner.Delegate = mOwner.delegate;
        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,
 }
 
 void ObjectAcl::cssmChangeOwner(const AclOwnerPrototype &newOwner,
@@ -357,6 +380,8 @@ void ObjectAcl::cssmChangeOwner(const AclOwnerPrototype &newOwner,
         
     // okay, replace it
     mOwner = newOwner;
         
     // okay, replace it
     mOwner = newOwner;
+
+    secinfo("SecAccess", "%p: new owner's type is %d", this, newOwner.subject().Head->WordID);
        
        changedAcl();
 
        
        changedAcl();
 
@@ -399,7 +424,7 @@ void ObjectAcl::clear()
 {
        mOwner = OwnerEntry();
        mEntries.erase(mEntries.begin(), mEntries.end());
 {
        mOwner = OwnerEntry();
        mEntries.erase(mEntries.begin(), mEntries.end());
-       secdebug("acl", "%p cleared", this);
+       secinfo("acl", "%p cleared", this);
 }
 
 
 }
 
 
@@ -413,6 +438,7 @@ void ObjectAcl::add(const std::string &tag, const AclEntry &newEntry)
 
 void ObjectAcl::add(const std::string &tag, AclEntry newEntry, CSSM_ACL_HANDLE handle)
 {
 
 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();
        //@@@ This should use a hook-registry mechanism. But for now, we are explicit:
        if (!newEntry.authorizesAnything) {
                for (AclAuthorizationSet::const_iterator it = newEntry.authorizations.begin();
@@ -460,9 +486,12 @@ bool ObjectAcl::OwnerEntry::authorizes(AclAuthorization) const
     return true;       // owner can do anything
 }
 
     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
 }
 
 
 }
 
 
@@ -507,12 +536,19 @@ bool ObjectAcl::AclEntry::authorizes(AclAuthorization auth) const
     return authorizesAnything || authorizations.find(auth) != authorizations.end();
 }
 
     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
 {
     //@@@ 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);
 void ObjectAcl::AclEntry::importBlob(Reader &pub, Reader &priv)
 {
     Entry::importBlob(pub, priv);
@@ -543,6 +579,7 @@ void ObjectAcl::AclEntry::importBlob(Reader &pub, Reader &priv)
 AclSubject::Maker::Maker(CSSM_ACL_SUBJECT_TYPE type)
        : mType(type)
 {
 AclSubject::Maker::Maker(CSSM_ACL_SUBJECT_TYPE type)
        : mType(type)
 {
+    StLock<Mutex> _(ObjectAcl::makersMutex);
     ObjectAcl::makers()[type] = this;
 }
 
     ObjectAcl::makers()[type] = this;
 }
 
@@ -561,6 +598,7 @@ AclSubject *ObjectAcl::make(uint32 typeAndVersion, Reader &pub, Reader &priv)
 
 AclSubject::Maker &ObjectAcl::makerFor(CSSM_ACL_SUBJECT_TYPE type)
 {
 
 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);
     AclSubject::Maker *maker = makers()[type];
     if (maker == NULL)
         CssmError::throwMe(CSSM_ERRCODE_ACL_SUBJECT_TYPE_NOT_SUPPORTED);