s4:passwords.py - add a python unittest for additional testing of my passwords work
authorMatthias Dieter Wallnöfer <mwallnoefer@yahoo.de>
Sat, 10 Apr 2010 18:04:13 +0000 (20:04 +0200)
committerMatthias Dieter Wallnöfer <mdw@samba.org>
Mon, 10 May 2010 17:12:27 +0000 (19:12 +0200)
This performs checks on direct password changes over LDB/LDAP. Indirect
password changes over the RPCs are already tested by some torture suite (SAMR
passwords). So no need to do this again here.

source4/lib/ldb/tests/python/passwords.py [new file with mode: 0755]
source4/selftest/tests.sh

diff --git a/source4/lib/ldb/tests/python/passwords.py b/source4/lib/ldb/tests/python/passwords.py
new file mode 100755 (executable)
index 0000000..9203e1b
--- /dev/null
@@ -0,0 +1,579 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# This tests the password changes over LDAP for AD implementations
+#
+# Copyright Matthias Dieter Wallnoefer 2010
+#
+# Notice: This tests will also work against Windows Server if the connection is
+# secured enough (SASL with a minimum of 128 Bit encryption) - consider
+# MS-ADTS 3.1.1.3.1.5
+#
+# Important: Make sure that the minimum password age is set to "0"!
+
+import optparse
+import sys
+import time
+import base64
+import os
+
+sys.path.append("bin/python")
+
+import samba.getopt as options
+
+from samba.auth import system_session
+from samba.credentials import Credentials
+from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError
+from ldb import ERR_NO_SUCH_OBJECT, ERR_ATTRIBUTE_OR_VALUE_EXISTS
+from ldb import ERR_ENTRY_ALREADY_EXISTS, ERR_UNWILLING_TO_PERFORM
+from ldb import ERR_NOT_ALLOWED_ON_NON_LEAF, ERR_OTHER, ERR_INVALID_DN_SYNTAX
+from ldb import ERR_NO_SUCH_ATTRIBUTE
+from ldb import ERR_OBJECT_CLASS_VIOLATION, ERR_NOT_ALLOWED_ON_RDN
+from ldb import ERR_NAMING_VIOLATION, ERR_CONSTRAINT_VIOLATION
+from ldb import ERR_UNDEFINED_ATTRIBUTE_TYPE
+from ldb import Message, MessageElement, Dn
+from ldb import FLAG_MOD_ADD, FLAG_MOD_REPLACE, FLAG_MOD_DELETE
+from samba import gensec
+from samba.samdb import SamDB
+from subunit.run import SubunitTestRunner
+import unittest
+
+parser = optparse.OptionParser("passwords [options] <host>")
+sambaopts = options.SambaOptions(parser)
+parser.add_option_group(sambaopts)
+parser.add_option_group(options.VersionOptions(parser))
+# use command line creds if available
+credopts = options.CredentialsOptions(parser)
+parser.add_option_group(credopts)
+opts, args = parser.parse_args()
+
+if len(args) < 1:
+    parser.print_usage()
+    sys.exit(1)
+
+host = args[0]
+
+lp = sambaopts.get_loadparm()
+creds = credopts.get_credentials(lp)
+
+# Force an encrypted connection
+creds.set_gensec_features(creds.get_gensec_features() | gensec.FEATURE_SEAL)
+
+#
+# Tests start here
+#
+
+class PasswordTests(unittest.TestCase):
+    def delete_force(self, ldb, dn):
+        try:
+            ldb.delete(dn)
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_NO_SUCH_OBJECT)
+
+    def find_basedn(self, ldb):
+        res = ldb.search(base="", expression="", scope=SCOPE_BASE,
+                         attrs=["defaultNamingContext"])
+        self.assertEquals(len(res), 1)
+        return res[0]["defaultNamingContext"][0]
+
+    def setUp(self):
+        self.ldb = ldb
+        self.base_dn = self.find_basedn(ldb)
+
+        # (Re)adds the test user "testuser" with the inital password
+        # "thatsAcomplPASS1"
+        self.delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
+        self.ldb.add({
+             "dn": "cn=testuser,cn=users," + self.base_dn,
+             "objectclass": ["user", "person"],
+             "sAMAccountName": "testuser",
+             "userPassword": "thatsAcomplPASS1" })
+        ldb.enable_account("(cn=testuser)" + self.base_dn)
+
+        # Open a second LDB connection with the user credentials. Use the
+        # command line credentials for informations like the domain, the realm
+        # and the workstation.
+        creds2 = Credentials()
+        # FIXME: Reactivate the user credentials when we have user password
+        # change support also on the ACL level in s4
+        creds2.set_username(creds.get_username())
+        creds2.set_password(creds.get_password())
+        #creds2.set_username("testuser")
+        #creds2.set_password("thatsAcomplPASS1")
+        creds2.set_domain(creds.get_domain())
+        creds2.set_realm(creds.get_realm())
+        creds2.set_workstation(creds.get_workstation())
+        creds2.set_gensec_features(creds2.get_gensec_features()
+                                                          | gensec.FEATURE_SEAL)
+        self.ldb2 = SamDB(url=host, credentials=creds2, lp=lp)
+
+    def test_unicodePwd_hash_set(self):
+        print "Performs a password hash set operation on 'unicodePwd' which should be prevented"
+        # Notice: Direct hash password sets should never work
+
+        m = Message()
+        m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
+        m["unicodePwd"] = MessageElement("XXXXXXXXXXXXXXXX", FLAG_MOD_REPLACE,
+          "unicodePwd")
+        try:
+            ldb.modify(m)
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+    def test_unicodePwd_hash_change(self):
+        print "Performs a password hash change operation on 'unicodePwd' which should be prevented"
+        # Notice: Direct hash password changes should never work
+
+        # Hash password changes should never work
+        try:
+            self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: unicodePwd
+unicodePwd: XXXXXXXXXXXXXXXX
+add: unicodePwd
+unicodePwd: YYYYYYYYYYYYYYYY
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+    def test_unicodePwd_clear_set(self):
+        print "Performs a password cleartext set operation on 'unicodePwd'"
+
+        m = Message()
+        m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
+        m["unicodePwd"] = MessageElement("\"thatsAcomplPASS2\"".encode('utf-16-le'),
+          FLAG_MOD_REPLACE, "unicodePwd")
+        ldb.modify(m)
+
+    def test_unicodePwd_clear_change(self):
+        print "Performs a password cleartext change operation on 'unicodePwd'"
+
+        self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: unicodePwd
+unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS1\"".encode('utf-16-le')) + """
+add: unicodePwd
+unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
+""")
+
+        # A change to the same password again will not work (password history)
+        try:
+            self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: unicodePwd
+unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
+add: unicodePwd
+unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+    def test_dBCSPwd_hash_set(self):
+        print "Performs a password hash set operation on 'dBCSPwd' which should be prevented"
+        # Notice: Direct hash password sets should never work
+
+        m = Message()
+        m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
+        m["dBCSPwd"] = MessageElement("XXXXXXXXXXXXXXXX", FLAG_MOD_REPLACE,
+          "dBCSPwd")
+        try:
+            ldb.modify(m)
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+    def test_dBCSPwd_hash_change(self):
+        print "Performs a password hash change operation on 'dBCSPwd' which should be prevented"
+        # Notice: Direct hash password changes should never work
+
+        try:
+            self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: dBCSPwd
+dBCSPwd: XXXXXXXXXXXXXXXX
+add: dBCSPwd
+dBCSPwd: YYYYYYYYYYYYYYYY
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+    def test_userPassword_clear_set(self):
+        print "Performs a password cleartext set operation on 'userPassword'"
+        # Notice: This works only against Windows if "dSHeuristics" has been set
+        # properly
+
+        m = Message()
+        m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
+        m["userPassword"] = MessageElement("thatsAcomplPASS2", FLAG_MOD_REPLACE,
+          "userPassword")
+        ldb.modify(m)
+
+    def test_userPassword_clear_change(self):
+        print "Performs a password cleartext change operation on 'userPassword'"
+        # Notice: This works only against Windows if "dSHeuristics" has been set
+        # properly
+
+        self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: userPassword
+userPassword: thatsAcomplPASS1
+add: userPassword
+userPassword: thatsAcomplPASS2
+""")
+
+        # A change to the same password again will not work (password history)
+        try:
+            self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: userPassword
+userPassword: thatsAcomplPASS2
+add: userPassword
+userPassword: thatsAcomplPASS2
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+    def test_clearTextPassword_clear_set(self):
+        print "Performs a password cleartext set operation on 'clearTextPassword'"
+        # Notice: This never works against Windows - only supported by us
+
+        try:
+            m = Message()
+            m.dn = Dn(ldb, "cn=testuser,cn=users," + self.base_dn)
+            m["clearTextPassword"] = MessageElement("thatsAcomplPASS2".encode('utf-16-le'),
+              FLAG_MOD_REPLACE, "clearTextPassword")
+            ldb.modify(m)
+            # this passes against s4
+        except LdbError, (num, msg):
+            # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
+            if num != ERR_NO_SUCH_ATTRIBUTE:
+                raise LdbError(num, msg)
+
+    def test_clearTextPassword_clear_change(self):
+        print "Performs a password cleartext change operation on 'clearTextPassword'"
+        # Notice: This never works against Windows - only supported by us
+
+        try:
+            self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: clearTextPassword
+clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS1".encode('utf-16-le')) + """
+add: clearTextPassword
+clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
+""")
+            # this passes against s4
+        except LdbError, (num, msg):
+            # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
+            if num != ERR_NO_SUCH_ATTRIBUTE:
+                raise LdbError(num, msg)
+
+        # A change to the same password again will not work (password history)
+        try:
+            self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: clearTextPassword
+clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
+add: clearTextPassword
+clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
+""")
+            self.fail()
+        except LdbError, (num, _):
+            # "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
+            if num != ERR_NO_SUCH_ATTRIBUTE:
+                self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+    def test_failures(self):
+        print "Performs some failure testing"
+
+        try:
+            ldb.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: userPassword
+userPassword: thatsAcomplPASS1
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+        try:
+            self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: userPassword
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+        try:
+            ldb.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+add: userPassword
+userPassword: thatsAcomplPASS1
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+        try:
+            self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+add: userPassword
+userPassword: thatsAcomplPASS1
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+        try:
+            ldb.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: userPassword
+userPassword: thatsAcomplPASS1
+add: userPassword
+userPassword: thatsAcomplPASS2
+userPassword: thatsAcomplPASS2
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+        try:
+            self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: userPassword
+userPassword: thatsAcomplPASS1
+add: userPassword
+userPassword: thatsAcomplPASS2
+userPassword: thatsAcomplPASS2
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+        try:
+            ldb.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: userPassword
+userPassword: thatsAcomplPASS1
+userPassword: thatsAcomplPASS1
+add: userPassword
+userPassword: thatsAcomplPASS2
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+        try:
+            self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: userPassword
+userPassword: thatsAcomplPASS1
+userPassword: thatsAcomplPASS1
+add: userPassword
+userPassword: thatsAcomplPASS2
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+        try:
+            ldb.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: userPassword
+userPassword: thatsAcomplPASS1
+add: userPassword
+userPassword: thatsAcomplPASS2
+add: userPassword
+userPassword: thatsAcomplPASS2
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+        try:
+            self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: userPassword
+userPassword: thatsAcomplPASS1
+add: userPassword
+userPassword: thatsAcomplPASS2
+add: userPassword
+userPassword: thatsAcomplPASS2
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+        try:
+            ldb.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: userPassword
+userPassword: thatsAcomplPASS1
+delete: userPassword
+userPassword: thatsAcomplPASS1
+add: userPassword
+userPassword: thatsAcomplPASS2
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+        try:
+            self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: userPassword
+userPassword: thatsAcomplPASS1
+delete: userPassword
+userPassword: thatsAcomplPASS1
+add: userPassword
+userPassword: thatsAcomplPASS2
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+        try:
+            ldb.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: userPassword
+userPassword: thatsAcomplPASS1
+add: userPassword
+userPassword: thatsAcomplPASS2
+replace: userPassword
+userPassword: thatsAcomplPASS3
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+        try:
+            self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: userPassword
+userPassword: thatsAcomplPASS1
+add: userPassword
+userPassword: thatsAcomplPASS2
+replace: userPassword
+userPassword: thatsAcomplPASS3
+""")
+            self.fail()
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
+
+        # Reverse order does work
+        self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+add: userPassword
+userPassword: thatsAcomplPASS2
+delete: userPassword
+userPassword: thatsAcomplPASS1
+""")
+
+        try:
+            self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: userPassword
+userPassword: thatsAcomplPASS2
+add: unicodePwd
+unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
+""")
+             # this passes against s4
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
+
+        try:
+            self.ldb2.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: unicodePwd
+unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS3\"".encode('utf-16-le')) + """
+add: userPassword
+userPassword: thatsAcomplPASS4
+""")
+             # this passes against s4
+        except LdbError, (num, _):
+            self.assertEquals(num, ERR_NO_SUCH_ATTRIBUTE)
+
+        # Several password changes at once are allowed
+        ldb.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+replace: userPassword
+userPassword: thatsAcomplPASS1
+userPassword: thatsAcomplPASS2
+""")
+
+        # Several password changes at once are allowed
+        ldb.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+replace: userPassword
+userPassword: thatsAcomplPASS1
+userPassword: thatsAcomplPASS2
+replace: userPassword
+userPassword: thatsAcomplPASS3
+replace: userPassword
+userPassword: thatsAcomplPASS4
+""")
+
+        # This surprisingly should work
+        self.delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
+        self.ldb.add({
+             "dn": "cn=testuser2,cn=users," + self.base_dn,
+             "objectclass": ["user", "person"],
+             "userPassword": ["thatsAcomplPASS1", "thatsAcomplPASS2"] })
+
+        # This surprisingly should work
+        self.delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
+        self.ldb.add({
+             "dn": "cn=testuser2,cn=users," + self.base_dn,
+             "objectclass": ["user", "person"],
+             "userPassword": ["thatsAcomplPASS1", "thatsAcomplPASS1"] })
+
+    def tearDown(self):
+        self.delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
+        self.delete_force(self.ldb, "cn=testuser2,cn=users," + self.base_dn)
+        # Close the second LDB connection (with the user credentials)
+        self.ldb2 = None
+
+if not "://" in host:
+    if os.path.isfile(host):
+        host = "tdb://%s" % host
+    else:
+        host = "ldap://%s" % host
+
+ldb = SamDB(url=host, session_info=system_session(), credentials=creds, lp=lp)
+
+runner = SubunitTestRunner()
+rc = 0
+if not runner.run(unittest.makeSuite(PasswordTests)).wasSuccessful():
+    rc = 1
+sys.exit(rc)
index f34612cc1590f0ba84763d6f8a514be72b8fbede..47bfba2bc50fb07bbf5952e16b0d6a048ca96bb8 100755 (executable)
@@ -491,6 +491,7 @@ plantestsuite "ldap_schema.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/pyt
 plantestsuite "ldap.possibleInferiors.python" dc $PYTHON $samba4srcdir/dsdb/samdb/ldb_modules/tests/possibleinferiors.py $CONFIGURATION ldap://\$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
 plantestsuite "ldap.secdesc.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python:../lib/testtools" $PYTHON $samba4srcdir/lib/ldb/tests/python/sec_descriptor.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
 plantestsuite "ldap.acl.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python:../lib/testtools" $PYTHON $samba4srcdir/lib/ldb/tests/python/acl.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
+plantestsuite "ldap.passwords.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python:../lib/testtools" $PYTHON $samba4srcdir/lib/ldb/tests/python/passwords.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN
 plantestsuite "xattr.python" none $SUBUNITRUN samba.tests.xattr
 plantestsuite "ntacls.python" none $SUBUNITRUN samba.tests.ntacls
 plantestsuite "deletetest.python" dc PYTHONPATH="$PYTHONPATH:../lib/subunit/python:../lib/testtools" $PYTHON $samba4srcdir/lib/ldb/tests/python/deletetest.py $CONFIGURATION \$SERVER -U\$USERNAME%\$PASSWORD -W \$DOMAIN