python/samba/netcmd/{forest.py,main.py}: add configuration controls
authorWilliam Brown <william@blackhats.net.au>
Wed, 25 Apr 2018 07:36:17 +0000 (17:36 +1000)
committerAndrew Bartlett <abartlet@samba.org>
Tue, 29 May 2018 03:34:07 +0000 (05:34 +0200)
With samba-tool we should expose ways to easily administer and control
common configuration options. This adds the base framework for modifying
forest settings, generally stored in cn=configuration partition.

An example is:

samba-tool forest directory_service show
samba-tool forest directory_service dsheuristics X

Signed-off-by: William Brown <william@blackhats.net.au>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
docs-xml/manpages/samba-tool.8.xml
python/samba/netcmd/forest.py [new file with mode: 0644]
python/samba/netcmd/main.py
python/samba/tests/samba_tool/forest.py [new file with mode: 0644]
source4/selftest/tests.py

index f2154b9d8deb34e7d4fc13fce48a77bd43e8fca2..72656d2d2a03d50d5257a9954d3fe41287b86da5 100644 (file)
        <para>Modify access list on a directory object.</para>
 </refsect3>
 
+<refsect2>
+       <title>forest</title>
+       <para>Manage Forest configuration.</para>
+</refsect2>
+
+<refsect3>
+       <title>forest directory_service</title>
+       <para>Manage directory_service behaviour for the forest.</para>
+</refsect3>
+
+<refsect3>
+       <title>forest directory_service dsheuristics <replaceable>VALUE</replaceable></title>
+       <para>Modify dsheuristics directory_service configuration for the forest.</para>
+</refsect3>
+
+<refsect3>
+       <title>forest directory_service show</title>
+       <para>Show current directory_service configuration for the forest.</para>
+</refsect3>
+
 <refsect2>
        <title>fsmo</title>
        <para>Manage Flexible Single Master Operations (FSMO).</para>
diff --git a/python/samba/netcmd/forest.py b/python/samba/netcmd/forest.py
new file mode 100644 (file)
index 0000000..9b4f676
--- /dev/null
@@ -0,0 +1,162 @@
+# domain management
+#
+# Copyright William Brown <william@blackhats.net.au> 2018
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import ldb
+import samba.getopt as options
+from samba.auth import system_session
+from samba.samdb import SamDB
+from samba.netcmd import (
+    Command,
+    CommandError,
+    SuperCommand,
+    Option
+    )
+
+class cmd_forest_show(Command):
+    """Display forest settings.
+
+    These settings control the behaviour of all domain controllers in this
+    forest. This displays those settings from the replicated configuration
+    partition.
+    """
+
+    synopsis = "%prog [options]"
+
+    takes_optiongroups = {
+        "sambaopts": options.SambaOptions,
+        "versionopts": options.VersionOptions,
+        "credopts": options.CredentialsOptions,
+        }
+
+    takes_options = [
+        Option("-H", "--URL", help="LDB URL for database or target server",
+                type=str, metavar="URL", dest="H"),
+        ]
+
+    def run(self, H=None, credopts=None, sambaopts=None, versionopts=None):
+        lp = sambaopts.get_loadparm()
+        creds = credopts.get_credentials(lp)
+
+        samdb = SamDB(url=H, session_info=system_session(),
+            credentials=creds, lp=lp)
+
+        domain_dn = samdb.domain_dn()
+        object_dn = "%s,%s" % (self.objectdn, domain_dn)
+
+        # Show all the settings we know how to set in the forest object!
+        res = samdb.search(base=object_dn, scope=ldb.SCOPE_BASE,
+                           attrs=self.attributes)
+
+        # Now we just display these attributes. The value is that
+        # we make them a bit prettier and human accessible.
+        # There should only be one response!
+        res_object = res[0]
+
+        self.outf.write("Settings for %s\n" % object_dn)
+        for attr in self.attributes:
+            try:
+                self.outf.write("%s: %s\n" % (attr, res_object[attr][0]))
+            except KeyError:
+                self.outf.write("%s: <NO VALUE>\n" % attr)
+
+class cmd_forest_set(Command):
+    """Modify forest settings.
+
+    This will alter the setting specified to value.
+    """
+
+    attribute = None
+    objectdn = None
+
+    synopsis = "%prog value [options]"
+
+    takes_optiongroups = {
+        "sambaopts": options.SambaOptions,
+        "versionopts": options.VersionOptions,
+        "credopts": options.CredentialsOptions,
+        }
+
+    takes_options = [
+        Option("-H", "--URL", help="LDB URL for database or target server",
+                type=str, metavar="URL", dest="H"),
+        ]
+
+    takes_args = ["value"]
+
+    def run(self, value, H=None, credopts=None, sambaopts=None, versionopts=None):
+        lp = sambaopts.get_loadparm()
+        creds = credopts.get_credentials(lp)
+
+        samdb = SamDB(url=H, session_info=system_session(),
+            credentials=creds, lp=lp)
+
+        domain_dn = samdb.domain_dn()
+        object_dn = "%s,%s" % (self.objectdn, domain_dn)
+
+        # Create the modification
+        m = ldb.Message()
+        m.dn = ldb.Dn(samdb, object_dn)
+        m[self.attribute] = ldb.MessageElement(
+            value, ldb.FLAG_MOD_REPLACE, self.attribute)
+
+        samdb.modify(m)
+        self.outf.write("set %s: %s\n" % (self.attribute, value))
+
+
+# Then you override it for each setting name:
+
+class cmd_forest_show_directory_service(cmd_forest_show):
+    """Display Directory Service settings for the forest.
+
+    These settings control how the Directory Service behaves on all domain
+    controllers in the forest.
+    """
+    objectdn = "CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration"
+    attributes = ['dsheuristics']
+
+class cmd_forest_set_directory_service_dsheuristics(cmd_forest_set):
+    """Set the value of dsheuristics on the Directory Service.
+
+    This value alters the behaviour of the Directory Service on all domain
+    controllers in the forest. Documentation related to this parameter can be
+    found here: https://msdn.microsoft.com/en-us/library/cc223560.aspx
+
+    In summary each "character" of the number-string, controls a setting.
+    A common setting is to set the value "2" in the 7th character. This controls
+    anonymous search behaviour.
+
+    Example: dsheuristics 0000002
+
+    This would allow anonymous LDAP searches to the domain (you may still need
+    to alter access controls to allow this).
+    """
+    objectdn = "CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration"
+    attribute = 'dsheuristics'
+
+class cmd_forest_directory_service(SuperCommand):
+    """Forest configuration partition management."""
+
+    subcommands = {}
+    subcommands["show"] = cmd_forest_show_directory_service()
+    subcommands["dsheuristics"] = cmd_forest_set_directory_service_dsheuristics()
+
+class cmd_forest(SuperCommand):
+    """Forest management."""
+
+    subcommands = {}
+    subcommands["directory_service"] = cmd_forest_directory_service()
index 40762fabdad802fcef76d2890b192a98864cdb97..56720801199a05f509824908e2bda61dbb4090f6 100644 (file)
@@ -63,6 +63,7 @@ class cmd_sambatool(SuperCommand):
     subcommands["domain"] = None
     subcommands["drs"] = None
     subcommands["dsacl"] = None
+    subcommands["forest"] = None
     subcommands["fsmo"] = None
     subcommands["gpo"] = None
     subcommands["group"] = None
diff --git a/python/samba/tests/samba_tool/forest.py b/python/samba/tests/samba_tool/forest.py
new file mode 100644 (file)
index 0000000..3e11d1f
--- /dev/null
@@ -0,0 +1,66 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) William Brown <william@blackhats.net.au> 2018
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import os
+import ldb
+from samba.tests.samba_tool.base import SambaToolCmdTest
+
+class ForestCmdTestCase(SambaToolCmdTest):
+    """Tests for samba-tool dsacl subcommands"""
+    samdb = None
+
+    def setUp(self):
+        super(ForestCmdTestCase, self).setUp()
+        self.samdb = self.getSamDB("-H", "ldap://%s" % os.environ["DC_SERVER"],
+            "-U%s%%%s" % (os.environ["DC_USERNAME"], os.environ["DC_PASSWORD"]))
+        self.domain_dn = self.samdb.domain_dn()
+
+    def tearDown(self):
+        super(ForestCmdTestCase, self).tearDown()
+        # Reset the values we might have changed.
+        ds_dn = "CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration"
+        m = ldb.Message()
+        m.dn = ldb.Dn(self.samdb, "%s,%s" % (ds_dn, self.domain_dn))
+        m['dsheuristics'] = ldb.MessageElement(
+            '0000000', ldb.FLAG_MOD_REPLACE, 'dsheuristics')
+
+        self.samdb.modify(m)
+
+    def test_display(self):
+        """Tests that we can display forest settings"""
+        (result, out, err) = self.runsublevelcmd("forest", ("directory_service",
+                              "show"),
+                              "-H", "ldap://%s" % os.environ["DC_SERVER"],
+                              "-U%s%%%s" % (os.environ["DC_USERNAME"],
+                                            os.environ["DC_PASSWORD"]))
+
+        self.assertCmdSuccess(result, out, err)
+        self.assertEquals(err,"","Shouldn't be any error messages")
+        self.assertIn("dsheuristics: <NO VALUE>", out)
+
+    def test_modify_dsheuristics(self):
+        """Test that we can modify the dsheuristics setting"""
+
+        (result, out, err) = self.runsublevelcmd("forest", ("directory_service",
+                              "dsheuristics"), "0000002",
+                              "-H", "ldap://%s" % os.environ["DC_SERVER"],
+                              "-U%s%%%s" % (os.environ["DC_USERNAME"],
+                                            os.environ["DC_PASSWORD"]))
+
+        self.assertCmdSuccess(result, out, err)
+        self.assertEquals(err,"","Shouldn't be any error messages")
+        self.assertIn("set dsheuristics: 0000002", out)
index b1d91ef99359226f96dd93253f01fe8f8533c96b..66b669e30f8f32318a13ee62591b6d9a4adf3935 100755 (executable)
@@ -619,6 +619,7 @@ planpythontestsuite("chgdcpass:local", "samba.tests.samba_tool.user_check_passwo
 planpythontestsuite("ad_dc_ntvfs:local", "samba.tests.samba_tool.group")
 planpythontestsuite("ad_dc_ntvfs:local", "samba.tests.samba_tool.ou")
 planpythontestsuite("ad_dc_ntvfs:local", "samba.tests.samba_tool.computer")
+planpythontestsuite("ad_dc_ntvfs:local", "samba.tests.samba_tool.forest")
 planpythontestsuite("ad_dc:local", "samba.tests.samba_tool.ntacl")
 planpythontestsuite("none", "samba.tests.samba_tool.provision_password_check")
 planpythontestsuite("none", "samba.tests.samba_tool.help")