samba-tool dsacl: Add subcommand to delete ACEs
authorChristian Merten <christian@merten.dev>
Tue, 13 Sep 2022 23:29:34 +0000 (01:29 +0200)
committerJeremy Allison <jra@samba.org>
Tue, 27 Sep 2022 16:46:35 +0000 (16:46 +0000)
A new subcommand has been added to samba-tool dsacl to delete one or multiple ACEs from the security
descriptor of an object.

Signed-off-by: Christian Merten <christian@merten.dev>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Jeremy Allison <jra@samba.org>
python/samba/netcmd/dsacl.py

index d3b8b5f554e34f4f1f8b9212a2703dc37c85f6d2..b4d3ddb2d3a82436cf54555358fa58e27e7aeedc 100644 (file)
@@ -227,9 +227,89 @@ class cmd_dsacl_get(Command):
         self.print_acl(samdb, objectdn)
 
 
+class cmd_dsacl_delete(Command):
+    """Delete an access list entry on a directory object."""
+
+    synopsis = "%prog [options]"
+
+    takes_optiongroups = {
+        "sambaopts": options.SambaOptions,
+        "credopts": options.CredentialsOptions,
+        "versionopts": options.VersionOptions,
+        }
+
+    takes_options = [
+        Option("-H", "--URL", help="LDB URL for database or target server",
+               type=str, metavar="URL", dest="H"),
+        Option("--objectdn", help="DN of the object whose SD to modify",
+            type="string"),
+        Option("--sddl", help="An ACE or group of ACEs to be deleted from the object",
+               type="string"),
+        ]
+
+    def print_acl(self, samdb, object_dn, new=False):
+        desc = self.read_descriptor(samdb, object_dn)
+        desc_sddl = desc.as_sddl(self.get_domain_sid(samdb))
+        if new:
+            self.outf.write("new descriptor for %s:\n" % object_dn)
+        else:
+            self.outf.write("old descriptor for %s:\n" % object_dn)
+        self.outf.write(desc_sddl + "\n")
+
+    def modify_descriptor(self, samdb, object_dn, desc, controls=None):
+        assert(isinstance(desc, security.descriptor))
+        m = ldb.Message()
+        m.dn = ldb.Dn(samdb, object_dn)
+        m["nTSecurityDescriptor"] = ldb.MessageElement(
+                (ndr_pack(desc)), ldb.FLAG_MOD_REPLACE,
+                "nTSecurityDescriptor")
+        samdb.modify(m)
+
+    def read_descriptor(self, samdb, object_dn):
+        res = samdb.search(base=object_dn, scope=SCOPE_BASE,
+                           attrs=["nTSecurityDescriptor"])
+        # we should theoretically always have an SD
+        assert(len(res) == 1)
+        desc = res[0]["nTSecurityDescriptor"][0]
+        return ndr_unpack(security.descriptor, desc)
+
+    def get_domain_sid(self, samdb):
+        res = samdb.search(base=samdb.domain_dn(),
+                           expression="(objectClass=*)", scope=SCOPE_BASE)
+        return ndr_unpack(security.dom_sid, res[0]["objectSid"][0])
+
+    def run(self, objectdn, sddl, H=None, credopts=None, sambaopts=None, versionopts=None):
+        lp = sambaopts.get_loadparm()
+        creds = credopts.get_credentials(lp)
+
+        if sddl is None or objectdn is None:
+            return self.usage()
+
+        samdb = SamDB(url=H, session_info=system_session(),
+                      credentials=creds, lp=lp)
+
+        self.print_acl(samdb, objectdn)
+        self.delete_ace(samdb, objectdn, sddl)
+        self.print_acl(samdb, objectdn, new=True)
+
+    def delete_ace(self, samdb, object_dn, delete_aces):
+        """Delete ace explicitly."""
+        desc = read_descriptor(samdb, object_dn)
+        domsid = get_domain_sid(samdb)
+        delete_aces = security.descriptor.from_sddl("D:" + delete_aces, domsid).dacl.aces
+        for ace in delete_aces:
+            if ace in desc.dacl.aces:
+                desc.dacl_del_ace(ace)
+            else:
+                sddl = ace.as_sddl(domsid)
+                self.outf.write("WARNING: (%s) was not found in the current security descriptor.\n" % sddl)
+        modify_descriptor(samdb, object_dn, desc)
+
+
 class cmd_dsacl(SuperCommand):
     """DS ACLs manipulation."""
 
     subcommands = {}
     subcommands["set"] = cmd_dsacl_set()
     subcommands["get"] = cmd_dsacl_get()
+    subcommands["delete"] = cmd_dsacl_delete()