Claims initial black box tests
authorRob van der Linde <rob@catalyst.net.nz>
Mon, 13 Nov 2023 10:48:52 +0000 (23:48 +1300)
committerDouglas Bagnall <dbagnall@samba.org>
Thu, 23 Nov 2023 00:32:33 +0000 (00:32 +0000)
Signed-off-by: Rob van der Linde <rob@catalyst.net.nz>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Autobuild-User(master): Douglas Bagnall <dbagnall@samba.org>
Autobuild-Date(master): Thu Nov 23 00:32:33 UTC 2023 on atb-devel-224

python/samba/tests/blackbox/claims.py [new file with mode: 0755]
selftest/knownfail.d/usage
source4/selftest/tests.py

diff --git a/python/samba/tests/blackbox/claims.py b/python/samba/tests/blackbox/claims.py
new file mode 100755 (executable)
index 0000000..135595e
--- /dev/null
@@ -0,0 +1,239 @@
+#!/usr/bin/env python3
+# Unix SMB/CIFS implementation.
+#
+# Blackbox tests for claims support
+#
+# Copyright (C) Catalyst.Net Ltd. 2023
+#
+# Written by Rob van der Linde <rob@catalyst.net.nz>
+#
+# 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
+
+from samba import NTSTATUSError
+from samba.auth import AuthContext
+from samba.credentials import Credentials
+from samba.gensec import FEATURE_SEAL, Security
+from samba.tests import BlackboxTestCase
+
+SERVER = os.environ["SERVER"]
+SERVER_USERNAME = os.environ["USERNAME"]
+SERVER_PASSWORD = os.environ["PASSWORD"]
+
+HOST = f"ldap://{SERVER}"
+CREDS = f"-U{SERVER_USERNAME}%{SERVER_PASSWORD}"
+
+
+class ClaimsSupportTests(BlackboxTestCase):
+    """Blackbox tests for Claims support
+
+    NOTE: all these commands are subcommands of samba-tool.
+
+    NOTE: the addCleanup functions get called automatically in reverse
+    order after the tests finishes, they don't execute straight away.
+    """
+
+    def test_user_group_access(self):
+        """An example use with groups."""
+        client_password = "T3stPassword0nly"
+        target_password = "T3stC0mputerPassword"
+
+        # Create a computer.
+        self.check_run("computer create claims-server")
+        self.addCleanup(self.run_command, "computer delete claims-server")
+        self.check_run(f"user setpassword claims-server\\$ --newpassword={target_password}")
+
+        # Create a user.
+        self.check_run(f"user create claimstestuser {client_password}")
+        self.addCleanup(self.run_command, "user delete claimstestuser")
+
+        # Create an authentication policy.
+        self.check_run("domain auth policy create --enforce --name=restricted-servers-pol")
+        self.addCleanup(self.run_command,
+                        "domain auth policy delete --name=restricted-servers-pol")
+
+        self.check_run("group add server-access-group")
+        self.addCleanup(self.run_command, "group delete server-access-group")
+
+        # Set allowed to authenticate to.
+        self.check_run(f"domain auth policy modify --name=restricted-servers-pol "
+                       "--computer-allowed-to-authenticate-to-by-group=server-access-group")
+
+        self.check_run("user auth policy assign claims-server\\$ --policy=restricted-servers-pol")
+
+        with self.assertRaises(NTSTATUSError) as error:
+            self.verify_access(
+                client_username="claimstestuser",
+                client_password=client_password,
+                target_hostname="claims-server",
+                target_username="claims-server",
+                target_password=target_password,
+            )
+
+        self.assertEqual(error.exception.args[0], 3221225581)
+        self.assertEqual(
+            error.exception.args[1],
+            "The attempted logon is invalid. This is either due to a "
+            "bad username or authentication information.")
+
+        # Add group members.
+        self.check_run("group addmembers server-access-group claimstestuser")
+
+        self.verify_access(
+            client_username="claimstestuser",
+            client_password=client_password,
+            target_hostname="claims-server",
+            target_username="claims-server",
+            target_password=target_password,
+        )
+
+    def test_user_silo_access(self):
+        """An example use with authentication silos."""
+        client_password = "T3stPassword0nly"
+        target_password = "T3stC0mputerPassword"
+
+        # Create a computer.
+        self.check_run("computer create claims-server")
+        self.addCleanup(self.run_command, "computer delete claims-server")
+        self.check_run(f"user setpassword claims-server\\$ --newpassword={target_password}")
+
+        # Create a user.
+        self.check_run(f"user create claimstestuser {client_password}")
+        self.addCleanup(self.run_command, "user delete claimstestuser")
+
+        # Create an authentication policy.
+        self.check_run("domain auth policy create --enforce --name=restricted-servers-pol")
+        self.addCleanup(self.run_command,
+                        "domain auth policy delete --name=restricted-servers-pol")
+
+        # Create an authentication silo.
+        self.check_run("domain auth silo create --enforce --name=restricted-servers-silo "
+                       "--user-authentication-policy=restricted-servers-pol "
+                       "--computer-authentication-policy=restricted-servers-pol "
+                       "--service-authentication-policy=restricted-servers-pol")
+        self.addCleanup(self.run_command,
+                        "domain auth silo delete --name=restricted-servers-silo")
+
+        # Set allowed to authenticate to.
+        self.check_run("domain auth policy modify --name=restricted-servers-pol "
+                       "--computer-allowed-to-authenticate-to-by-silo=restricted-servers-silo")
+
+        # Grant access to silo.
+        self.check_run("domain auth silo member grant --name=restricted-servers-silo --member=claims-server\\$")
+        self.check_run("domain auth silo member grant --name=restricted-servers-silo --member=claimstestuser")
+
+        self.verify_access(
+            client_username="claimstestuser",
+            client_password=client_password,
+            target_hostname="claims-server",
+            target_username="claims-server",
+            target_password=target_password,
+        )
+
+        self.check_run("user auth silo assign claims-server\\$ --silo=restricted-servers-silo")
+
+        with self.assertRaises(NTSTATUSError) as error:
+            self.verify_access(
+                client_username="claimstestuser",
+                client_password=client_password,
+                target_hostname="claims-server",
+                target_username="claims-server",
+                target_password=target_password,
+            )
+
+        self.assertEqual(error.exception.args[0], 3221225581)
+        self.assertEqual(
+            error.exception.args[1],
+            "The attempted logon is invalid. This is either due to a "
+            "bad username or authentication information.")
+
+        # Set assigned silo on user and computer.
+        self.check_run("user auth silo assign claimstestuser --silo=restricted-servers-silo")
+
+        self.verify_access(
+            client_username="claimstestuser",
+            client_password=client_password,
+            target_hostname="claims-server",
+            target_username="claims-server",
+            target_password=target_password,
+        )
+
+    @classmethod
+    def _make_cmdline(cls, line):
+        """Override to pass line as samba-tool subcommand instead.
+
+        Automatically fills in HOST and CREDS as well.
+        """
+        if isinstance(line, list):
+            cmd = ["samba-tool"] + line + ["-H", HOST, CREDS]
+        else:
+            cmd = f"samba-tool {line} -H {HOST} {CREDS}"
+
+        return super()._make_cmdline(cmd)
+
+    def verify_access(self, client_username, client_password,
+                      target_hostname, target_username, target_password):
+
+        lp = self.get_loadparm()
+
+        client_creds = Credentials()
+        client_creds.set_username(client_username)
+        client_creds.set_password(client_password)
+        client_creds.guess(lp)
+
+        target_creds = Credentials()
+        target_creds.set_username(target_username)
+        target_creds.set_password(target_password)
+        target_creds.guess(lp)
+
+        settings = {
+            "lp_ctx": lp,
+            "target_hostname": target_hostname
+        }
+
+        gensec_client = Security.start_client(settings)
+        gensec_client.set_credentials(client_creds)
+        gensec_client.want_feature(FEATURE_SEAL)
+        gensec_client.start_mech_by_sasl_name("GSSAPI")
+
+        gensec_target = Security.start_server(settings=settings,
+                                              auth_context=AuthContext(lp_ctx=lp))
+        gensec_target.set_credentials(target_creds)
+        gensec_target.start_mech_by_sasl_name("GSSAPI")
+
+        client_finished = False
+        server_finished = False
+        client_to_server = b""
+        server_to_client = b""
+
+        # Operate as both the client and the server to verify the user's
+        # credentials.
+        while not client_finished or not server_finished:
+            if not client_finished:
+                print("running client gensec_update")
+                client_finished, client_to_server = gensec_client.update(
+                    server_to_client)
+            if not server_finished:
+                print("running server gensec_update")
+                server_finished, server_to_client = gensec_target.update(
+                    client_to_server)
+
+        print("Got ticket to server we were set not to get a ticket to, and decrypted it")
+
+
+if __name__ == "__main__":
+    import unittest
+    unittest.main()
index e7d707f0e7db13d30339e0f39f0165adb2982a25..844a97dd34da40d225136174511f270e921c73d6 100644 (file)
@@ -1,3 +1,4 @@
+samba.tests.usage.samba.tests.usage.PythonScriptHelpTests.test_claims_py
 samba.tests.usage.samba.tests.usage.PythonScriptHelpTests.test_autobuild_py.none.
 samba.tests.usage.samba.tests.usage.PythonScriptHelpTests.test_compare_cc_results_py.none.
 samba.tests.usage.samba.tests.usage.PythonScriptHelpTests.test_config_base.none.
@@ -11,6 +12,7 @@ samba.tests.usage.samba.tests.usage.PythonScriptHelpTests.test_smbstatus.none.
 samba.tests.usage.samba.tests.usage.PythonScriptHelpTests.test_tests_py.none.
 samba.tests.usage.samba.tests.usage.PythonScriptHelpTests.test_tests_py_.none.
 samba.tests.usage.samba.tests.usage.PythonScriptHelpTests.test_waf.none.
+samba.tests.usage.samba.tests.usage.PythonScriptUsageTests.test_claims_py
 samba.tests.usage.samba.tests.usage.PythonScriptUsageTests.test_chgtdcpass.none.
 samba.tests.usage.samba.tests.usage.PythonScriptUsageTests.test_findprovisionusnranges.none.
 samba.tests.usage.samba.tests.usage.PythonScriptUsageTests.test_machineaccountpw.none.
index bdb6a8660fdd133f63cafc2fc3dbcecf85725dfb..f5a4107aa52b898a641d277d19b4fefdc042963d 100755 (executable)
@@ -605,6 +605,8 @@ plantestsuite("samba4.blackbox.test_alias_membership", "ad_member_idmap_rid:loca
 
 plantestsuite("samba4.blackbox.test_old_enctypes", "fl2003dc:local", [os.path.join(bbdir, "test_old_enctypes.sh"), '$SERVER', '$USERNAME', '$PASSWORD', '$NETBIOSNAME', '$PREFIX_ABS'])
 
+planpythontestsuite("ad_dc_default", "samba.tests.blackbox.claims")
+
 if have_heimdal_support:
     plantestsuite("samba4.blackbox.kpasswd",
                   "ad_dc:local",