s4:dsdb:acl_read: Implement "List Object" mode feature
authorStefan Metzmacher <metze@samba.org>
Tue, 13 Oct 2020 10:43:39 +0000 (12:43 +0200)
committerStefan Metzmacher <metze@samba.org>
Wed, 21 Oct 2020 08:48:01 +0000 (08:48 +0000)
See [MS-ADTS] 5.1.3.3.6 Checking Object Visibility

I tried to avoid any possible overhead for the common cases:

- SEC_ADS_LIST (List Children) is already granted by default
- fDoListObject is off by default

Overhead is only added if the administrator turned on
the fDoListObject feature and removed SEC_ADS_LIST (List Children)
from a parent object.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14531

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Autobuild-User(master): Stefan Metzmacher <metze@samba.org>
Autobuild-Date(master): Wed Oct 21 08:48:02 UTC 2020 on sn-devel-184

selftest/knownfail.d/ldap-acl-visibility [deleted file]
source4/dsdb/samdb/ldb_modules/acl_read.c

diff --git a/selftest/knownfail.d/ldap-acl-visibility b/selftest/knownfail.d/ldap-acl-visibility
deleted file mode 100644 (file)
index b580b2e..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_CO_CO_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_CO_Cn_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_CO_nO_CO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_CO_nO_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_CO_nn_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_Cn_CO_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_Cn_Cn_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_Cn_nO_CO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_Cn_nO_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_Cn_nn_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_nO_CO_CO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_nO_CO_Cn
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_nO_CO_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_nO_CO_nn
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_nO_Cn_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_nO_nO_CO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_nO_nO_Cn
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_nO_nO_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_nO_nO_nn
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_nO_nn_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_nn_CO_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_nn_Cn_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_nn_nO_CO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_nn_nO_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Allow_nn_nn_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_CO_CO_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_CO_Cn_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_CO_nO_CO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_CO_nO_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_CO_nn_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_Cn_CO_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_Cn_Cn_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_Cn_nO_CO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_Cn_nO_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_Cn_nn_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_nO_CO_CO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_nO_CO_Cn
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_nO_CO_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_nO_CO_nn
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_nO_Cn_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_nO_nO_CO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_nO_nO_Cn
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_nO_nO_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_nO_nO_nn
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_nO_nn_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_nn_CO_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_nn_Cn_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_nn_nO_CO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_nn_nO_nO
-^samba4.ldap.acl.python.*.__main__.AclVisibiltyTests.test_visibility_Do_Deny_nn_nn_nO
index f217288f3a953ecf00f051fe983f76287488d998..b221dcde445bb8c3d0b56b8221141bba63ad4370 100644 (file)
@@ -52,6 +52,8 @@ struct aclread_context {
        bool added_objectClass;
        bool indirsync;
 
+       bool do_list_object_initialized;
+       bool do_list_object;
        bool base_invisible;
        uint64_t num_entries;
 
@@ -162,6 +164,7 @@ static int aclread_check_object_visible(struct aclread_context *ac,
                                        struct ldb_request *req)
 {
        uint32_t instanceType;
+       int ret;
 
        /* get the object instance type */
        instanceType = ldb_msg_find_attr_as_uint(msg,
@@ -173,7 +176,81 @@ static int aclread_check_object_visible(struct aclread_context *ac,
                return LDB_SUCCESS;
        }
 
-       return aclread_check_parent(ac, msg, req);
+       ret = aclread_check_parent(ac, msg, req);
+       if (ret == LDB_SUCCESS) {
+               /*
+                * SEC_ADS_LIST (List Children) alone
+                * on the parent is enough to make the
+                * object visible.
+                */
+               return LDB_SUCCESS;
+       }
+       if (ret != LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
+               return ret;
+       }
+
+       if (!ac->do_list_object_initialized) {
+               /*
+                * We only call dsdb_do_list_object() once
+                * and only when needed in order to
+                * check the dSHeuristics for fDoListObject.
+                */
+               ac->do_list_object = dsdb_do_list_object(ac->module, ac, req);
+               ac->do_list_object_initialized = true;
+       }
+
+       if (ac->do_list_object) {
+               TALLOC_CTX *frame = talloc_stackframe();
+               struct ldb_dn *parent_dn = NULL;
+
+               /*
+                * Here we're in "List Object" mode (fDoListObject=true).
+                *
+                * If SEC_ADS_LIST (List Children) is not
+                * granted on the parent, we need to check if
+                * SEC_ADS_LIST_OBJECT (List Object) is granted
+                * on the parent and also on the object itself.
+                *
+                * We could optimize this similar to aclread_check_parent(),
+                * but that would require quite a bit of restructuring,
+                * so that we cache the granted access bits instead
+                * of just the result for 'SEC_ADS_LIST (List Children)'.
+                *
+                * But as this is the uncommon case and
+                * 'SEC_ADS_LIST (List Children)' is most likely granted
+                * on most of the objects, we'll just implement what
+                * we have to.
+                */
+
+               parent_dn = ldb_dn_get_parent(frame, msg->dn);
+               if (parent_dn == NULL) {
+                       TALLOC_FREE(frame);
+                       return ldb_oom(ldb_module_get_ctx(ac->module));
+               }
+               ret = dsdb_module_check_access_on_dn(ac->module,
+                                                    frame,
+                                                    parent_dn,
+                                                    SEC_ADS_LIST_OBJECT,
+                                                    NULL, req);
+               if (ret != LDB_SUCCESS) {
+                       TALLOC_FREE(frame);
+                       return ret;
+               }
+               ret = dsdb_module_check_access_on_dn(ac->module,
+                                                    frame,
+                                                    msg->dn,
+                                                    SEC_ADS_LIST_OBJECT,
+                                                    NULL, req);
+               if (ret != LDB_SUCCESS) {
+                       TALLOC_FREE(frame);
+                       return ret;
+               }
+
+               TALLOC_FREE(frame);
+               return LDB_SUCCESS;
+       }
+
+       return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
 }
 
 /*