[SCM] Samba Shared Repository - branch master updated

Andrew Bartlett abartlet at samba.org
Thu Apr 6 01:34:01 UTC 2023


The branch, master has been updated
       via  112faff82f9 dsdb: modify unicodePwd requires encrypted connection
       via  928de1d61c8 dsdb/tests: Add test for modification of unicodePwd over a cleartext/signed connection
       via  5abda27f0e2 dsdb: fix spelling in password_hash.c
       via  479634e4cd6 dsdb/tests: Double number of expressions in large_ldap.py ldap_timeout test
       via  e1c0c2066c2 dsdb/tests: Move SD modification on class-created objects to classSetUp
      from  b74b9f4b06c CVE-2023-0922 set default ldap client sasl wrapping to seal

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 112faff82f93f9b16f67905c5cbdd5806bd7c214
Author: Rob van der Linde <rob at catalyst.net.nz>
Date:   Mon Feb 20 11:50:36 2023 +1300

    dsdb: modify unicodePwd requires encrypted connection
    
    Signed-off-by: Rob van der Linde <rob at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Joseph Sutton <josephsutton at catalyst.net.nz>
    
    Autobuild-User(master): Andrew Bartlett <abartlet at samba.org>
    Autobuild-Date(master): Thu Apr  6 01:33:05 UTC 2023 on atb-devel-224

commit 928de1d61c884c7691b57fbe5fffa8f792ce68fd
Author: Rob van der Linde <rob at catalyst.net.nz>
Date:   Wed Apr 5 12:30:03 2023 +1200

    dsdb/tests: Add test for modification of unicodePwd over a cleartext/signed connection
    
    This demonstrates that the server did not detect CVE-2023-0922
    
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Signed-off-by: Rob van der Linde <rob at catalyst.net.nz>
    Reviewed-by: Joseph Sutton <josephsutton at catalyst.net.nz>

commit 5abda27f0e2db9738f81c86a25929462ed6189ce
Author: Rob van der Linde <rob at catalyst.net.nz>
Date:   Thu Feb 16 13:23:42 2023 +1300

    dsdb: fix spelling in password_hash.c
    
    Signed-off-by: Rob van der Linde <rob at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Joseph Sutton <josephsutton at catalyst.net.nz>

commit 479634e4cd6543d489eb4700aebde1a479b94fe5
Author: Andrew Bartlett <abartlet at samba.org>
Date:   Thu Apr 6 08:59:17 2023 +1200

    dsdb/tests: Double number of expressions in large_ldap.py ldap_timeout test
    
    By slowing the filter down more this makes the test reliable on the
    autobuild host.
    
    This is not a long-term solution, but is a quick tweak that can be done
    today to address current issues with getting commits past the host-based
    (compared with cloud-based) autobuild.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15351
    
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Joseph Sutton <josephsutton at catalyst.net.nz>

commit e1c0c2066c2f29bb614e3386b796eec3cb289aea
Author: Andrew Bartlett <abartlet at samba.org>
Date:   Thu Apr 6 08:54:02 2023 +1200

    dsdb/tests: Move SD modification on class-created objects to classSetUp
    
    These modifications persist, so should be done at the class level,
    not in the test.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15351
    
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Joseph Sutton <josephsutton at catalyst.net.nz>

-----------------------------------------------------------------------

Summary of changes:
 source4/dsdb/samdb/ldb_modules/password_hash.c    |  24 +++-
 source4/dsdb/samdb/samdb.h                        |   5 +
 source4/dsdb/tests/python/large_ldap.py           |  20 ++-
 source4/dsdb/tests/python/unicodepwd_encrypted.py | 151 ++++++++++++++++++++++
 source4/ldap_server/ldap_backend.c                |  23 ++++
 source4/selftest/tests.py                         |   1 +
 6 files changed, 211 insertions(+), 13 deletions(-)
 create mode 100644 source4/dsdb/tests/python/unicodepwd_encrypted.py


Changeset truncated at 500 lines:

diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c
index 6a713b86736..417e34b79e6 100644
--- a/source4/dsdb/samdb/ldb_modules/password_hash.c
+++ b/source4/dsdb/samdb/ldb_modules/password_hash.c
@@ -252,7 +252,7 @@ static int password_hash_bypass(struct ldb_module *module, struct ldb_request *r
 	GET_VALUES(nte, "unicodePwd");
 
 	/*
-	 * Even as Samba contiuues to ignore the LM hash, and reset it
+	 * Even as Samba continues to ignore the LM hash, and reset it
 	 * when practical, we keep the constraint that it must be a 16
 	 * byte value if specified.
 	 */
@@ -2869,6 +2869,8 @@ static int check_password_restrictions(struct setup_password_fields_io *io, WERR
 	struct loadparm_context *lp_ctx =
 		talloc_get_type(ldb_get_opaque(ldb, "loadparm"),
 				struct loadparm_context);
+	struct dsdb_encrypted_connection_state *opaque_connection_state =
+		ldb_get_opaque(ldb,DSDB_OPAQUE_ENCRYPTED_CONNECTION_STATE_NAME);
 
 	*werror = WERR_INVALID_PARAMETER;
 
@@ -2876,10 +2878,28 @@ static int check_password_restrictions(struct setup_password_fields_io *io, WERR
 		return LDB_SUCCESS;
 	}
 
+	/*
+	 * Prevent update password on an insecure connection.
+	 * The opaque is added in the ldap backend init.
+	 */
+	if (opaque_connection_state != NULL &&
+	    !opaque_connection_state->using_encrypted_connection) {
+		ret = LDB_ERR_UNWILLING_TO_PERFORM;
+		*werror = WERR_GEN_FAILURE;
+		ldb_asprintf_errstring(ldb,
+				       "%08X: SvcErr: DSID-031A126C, "
+				       "problem 5003 (WILL_NOT_PERFORM), "
+				       "data 0\n"
+				       "Password modification over LDAP "
+				       "must be over an encrypted connection",
+				       W_ERROR_V(*werror));
+		return ret;
+	}
+
 	/*
 	 * First check the old password is correct, for password
 	 * changes when this hasn't already been checked by a
-	 * trustwrothy layer above
+	 * trustworthy layer above
 	 */
 	if (!io->ac->pwd_reset && !(io->ac->change
 				    && io->ac->change->old_password_checked == DSDB_PASSWORD_CHECKED_AND_CORRECT)) {
diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h
index 8bc67301a98..7ca6d26f67a 100644
--- a/source4/dsdb/samdb/samdb.h
+++ b/source4/dsdb/samdb/samdb.h
@@ -375,6 +375,11 @@ struct dsdb_extended_dn_store_format {
 
 #define DSDB_FULL_JOIN_REPLICATION_COMPLETED_OPAQUE_NAME "DSDB_FULL_JOIN_REPLICATION_COMPLETED"
 
+#define DSDB_OPAQUE_ENCRYPTED_CONNECTION_STATE_NAME "DSDB_OPAQUE_ENCRYPTED_CONNECTION_STATE_MSG"
+struct dsdb_encrypted_connection_state {
+	bool using_encrypted_connection;
+};
+
 #define DSDB_SAMDB_MINIMUM_ALLOWED_RID   1000
 
 #define DSDB_METADATA_SCHEMA_SEQ_NUM	"SCHEMA_SEQ_NUM"
diff --git a/source4/dsdb/tests/python/large_ldap.py b/source4/dsdb/tests/python/large_ldap.py
index 9f9e23fb76e..02472baef5a 100644
--- a/source4/dsdb/tests/python/large_ldap.py
+++ b/source4/dsdb/tests/python/large_ldap.py
@@ -145,6 +145,14 @@ class LargeLDAPTest(samba.tests.TestCase):
                 "sAMAccountName": user_name,
                 "jpegPhoto": b'a' * (2 * 1024 * 1024)})
 
+            ace = "(OD;;RP;{6bc69afa-7bd9-4184-88f5-28762137eb6a};;S-1-%d)" % x
+            dn = ldb.Dn(cls.ldb, "cn=" + user_name + "," + str(cls.ou_dn))
+
+            # add an ACE that denies access to the above random attr
+            # for a not-existing user.  This makes each SD distinct
+            # and so will slow SD parsing.
+            cls.sd_utils.dacl_add_ace(dn, ace)
+
     @classmethod
     def tearDownClass(cls):
         # Remake the connection for tear-down (old Samba drops the socket)
@@ -289,19 +297,9 @@ class LargeLDAPTest(samba.tests.TestCase):
                       session_info=system_session(lp),
                       lp=lp)
 
-        for x in range(200):
-            user_name = self.USER_NAME + format(x, "03")
-            ace = "(OD;;RP;{6bc69afa-7bd9-4184-88f5-28762137eb6a};;S-1-%d)" % x
-            dn = ldb.Dn(self.ldb, "cn=" + user_name + "," + str(self.ou_dn))
-
-            # add an ACE that denies access to the above random attr
-            # for a not-existing user.  This makes each SD distinct
-            # and so will slow SD parsing.
-            self.sd_utils.dacl_add_ace(dn, ace)
-
         # Create a large search expression that will take a long time to
         # evaluate.
-        expression = f'(jpegPhoto=*X*)' * 1000
+        expression = '(jpegPhoto=*X*)' * 2000
         expression = f'(|{expression})'
 
         # Perform the LDAP search.
diff --git a/source4/dsdb/tests/python/unicodepwd_encrypted.py b/source4/dsdb/tests/python/unicodepwd_encrypted.py
new file mode 100644
index 00000000000..768cbf83f8e
--- /dev/null
+++ b/source4/dsdb/tests/python/unicodepwd_encrypted.py
@@ -0,0 +1,151 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+import sys
+import optparse
+
+sys.path.insert(0, "bin/python")
+import samba.getopt as options
+from ldb import Message, MessageElement, Dn
+from ldb import LdbError, FLAG_MOD_REPLACE, ERR_UNWILLING_TO_PERFORM, SCOPE_BASE
+from samba import gensec
+from samba.auth import system_session
+from samba.samdb import SamDB
+from samba.tests import delete_force
+from samba.tests.password_test import PasswordTestCase
+from samba.tests.subunitrun import SubunitOptions, TestProgram
+
+parser = optparse.OptionParser("unicodepwd_encrypted.py [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)
+subunitopts = SubunitOptions(parser)
+parser.add_option_group(subunitopts)
+lp = sambaopts.get_loadparm()
+opts, args = parser.parse_args()
+
+if len(args) < 1:
+    parser.print_usage()
+    sys.exit(1)
+
+host = args[0]
+host_ldaps = f"ldaps://{host}"
+host_ldap = f"ldap://{host}"
+
+
+class UnicodePwdEncryptedConnectionTests(PasswordTestCase):
+
+    def setUp(self):
+        super().setUp()
+        self.creds = self.insta_creds(template=credopts.get_credentials(lp))
+        self.ldb = SamDB(host_ldap, credentials=self.creds,
+                         session_info=system_session(lp),
+                         lp=lp)
+        self.base_dn = self.ldb.domain_dn()
+        self.user_dn_str = f"cn=testuser,cn=users,{self.base_dn}"
+        self.user_dn = Dn(self.ldb, self.user_dn_str)
+        print(f"baseDN: {self.base_dn}\n")
+
+        # permit password changes during this test
+        self.allow_password_changes()
+
+        # (Re)adds the test user "testuser" with no password.
+        delete_force(self.ldb, str(self.user_dn))
+        self.ldb.add({
+            "dn": str(self.user_dn),
+            "objectclass": "user",
+            "sAMAccountName": "testuser"
+        })
+
+        # Set the test user initial password and enable account.
+        m = Message(self.user_dn)
+        m["0"] = MessageElement("Password#2", FLAG_MOD_REPLACE, "userPassword")
+        self.ldb.modify(m)
+        self.ldb.enable_account("(sAMAccountName=testuser)")
+
+    def modify_unicode_pwd(self, ldb, password):
+        """Replaces user password using unicodePwd."""
+        m = Message()
+        m.dn = self.user_dn
+        m["unicodePwd"] = MessageElement(
+            f'"{password}"'.encode('utf-16-le'),
+            FLAG_MOD_REPLACE, "unicodePwd"
+        )
+        ldb.modify(m)
+
+    def get_admin_sid(self, ldb):
+        res = self.ldb.search(
+            base="", expression="", scope=SCOPE_BASE, attrs=["tokenGroups"])
+
+        return self.ldb.schema_format_value(
+            "tokenGroups", res[0]["tokenGroups"][0]).decode("utf8")
+
+    def test_with_seal(self):
+        """Test unicodePwd on connection with seal.
+
+        This should allow unicodePwd.
+        """
+        self.modify_unicode_pwd(self.ldb, "thatsAcomplPASS2")
+
+    def test_without_seal(self):
+        """Test unicodePwd on connection without seal.
+
+        Should not allow unicodePwd on an unencrypted connection.
+
+        Requires --use-kerberos=required, or it automatically upgrades
+        to an encrypted connection.
+        """
+        # Remove FEATURE_SEAL which gets added by insta_creds.
+        creds_noseal = self.insta_creds(template=credopts.get_credentials(lp))
+        creds_noseal.set_gensec_features(creds_noseal.get_gensec_features() &
+                                         ~gensec.FEATURE_SEAL)
+
+        sasl_wrap = lp.get('client ldap sasl wrapping')
+        self.addCleanup(lp.set, 'client ldap sasl wrapping', sasl_wrap)
+        lp.set('client ldap sasl wrapping', 'sign')
+
+        # Create a second ldb connection without seal.
+        ldb = SamDB(host_ldap, credentials=creds_noseal,
+                    session_info=system_session(lp),
+                    lp=lp)
+
+        with self.assertRaises(LdbError) as e:
+            self.modify_unicode_pwd(ldb, "thatsAcomplPASS2")
+
+        # Server should not allow unicodePwd on an unencrypted connection.
+        self.assertEqual(e.exception.args[0], ERR_UNWILLING_TO_PERFORM)
+        self.assertIn(
+            "Password modification over LDAP must be over an encrypted connection",
+            e.exception.args[1]
+        )
+
+    def test_simple_bind_plain(self):
+        """Test unicodePwd using simple bind without encryption."""
+        admin_sid = self.get_admin_sid(self.ldb)
+
+        self.creds.set_bind_dn(admin_sid)
+        ldb = SamDB(url=host_ldap, credentials=self.creds, lp=lp)
+
+        with self.assertRaises(LdbError) as e:
+            self.modify_unicode_pwd(ldb, "thatsAcomplPASS2")
+
+        # Server should not allow unicodePwd on an unencrypted connection.
+        self.assertEqual(e.exception.args[0], ERR_UNWILLING_TO_PERFORM)
+        self.assertIn(
+            "Password modification over LDAP must be over an encrypted connection",
+            e.exception.args[1]
+        )
+
+    def test_simple_bind_tls(self):
+        """Test unicodePwd using simple bind with encryption."""
+        admin_sid = self.get_admin_sid(self.ldb)
+
+        self.creds.set_bind_dn(admin_sid)
+        ldb = SamDB(url=host_ldaps, credentials=self.creds, lp=lp)
+
+        self.modify_unicode_pwd(ldb, "thatsAcomplPASS2")
+
+
+TestProgram(module=__name__, opts=subunitopts)
diff --git a/source4/ldap_server/ldap_backend.c b/source4/ldap_server/ldap_backend.c
index 9429ba1561f..8db85c58fac 100644
--- a/source4/ldap_server/ldap_backend.c
+++ b/source4/ldap_server/ldap_backend.c
@@ -186,6 +186,11 @@ static int map_ldb_error(TALLOC_CTX *mem_ctx, int ldb_err,
 int ldapsrv_backend_Init(struct ldapsrv_connection *conn,
 			      char **errstring)
 {
+	bool using_tls = conn->sockets.active == conn->sockets.tls;
+	bool using_seal = conn->gensec != NULL && gensec_have_feature(conn->gensec,
+								      GENSEC_FEATURE_SEAL);
+	struct dsdb_encrypted_connection_state *opaque_connection_state = NULL;
+
 	int ret = samdb_connect_url(conn,
 				    conn->connection->event.ctx,
 				    conn->lp_ctx,
@@ -199,6 +204,24 @@ int ldapsrv_backend_Init(struct ldapsrv_connection *conn,
 		return ret;
 	}
 
+	/*
+	 * We can safely call ldb_set_opaque() on this ldb as we have
+	 * set remote_address above which avoids the ldb handle cache
+	 */
+	opaque_connection_state = talloc_zero(conn, struct dsdb_encrypted_connection_state);
+	if (opaque_connection_state == NULL) {
+		return LDB_ERR_OPERATIONS_ERROR;
+	}
+	opaque_connection_state->using_encrypted_connection = using_tls || using_seal;
+	ret = ldb_set_opaque(conn->ldb,
+			     DSDB_OPAQUE_ENCRYPTED_CONNECTION_STATE_NAME,
+			     opaque_connection_state);
+	if (ret != LDB_SUCCESS) {
+		DBG_ERR("ldb_set_opaque() failed to store our "
+			"encrypted connection state!");
+		return ret;
+	}
+
 	if (conn->server_credentials) {
 		struct gensec_security *gensec_security = NULL;
 		const char **sasl_mechs = NULL;
diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index f348adf347d..fc38c09966d 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -1321,6 +1321,7 @@ plantestsuite("samba4.asq.python(ad_dc_default)", "ad_dc_default", [python, os.p
 plantestsuite("samba4.user_account_control.python(ad_dc_default)", "ad_dc_default", [python, os.path.join(DSDB_PYTEST_DIR, "user_account_control.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN'])
 plantestsuite("samba4.priv_attrs.python(ad_dc_default)", "ad_dc_default", ["STRICT_CHECKING=0", python, os.path.join(DSDB_PYTEST_DIR, "priv_attrs.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN'])
 plantestsuite("samba4.priv_attrs.strict.python(ad_dc_default)", "ad_dc_default", [python, os.path.join(DSDB_PYTEST_DIR, "priv_attrs.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN'])
+plantestsuite("samba4.unicodepwd_encrypted(fl2008r2dc)", "fl2008r2dc", [python, os.path.join(DSDB_PYTEST_DIR, "unicodepwd_encrypted.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN'])
 
 for env in ['ad_dc_default:local', 'schema_dc:local']:
     planoldpythontestsuite(env, "dsdb_schema_info",


-- 
Samba Shared Repository



More information about the samba-cvs mailing list