[SCM] Samba Shared Repository - branch master updated

Jo Sutton jsutton at samba.org
Sun Apr 21 23:18:02 UTC 2024


The branch, master has been updated
       via  20ce68f1594 tests/krb5: Test retrieving a denied gMSA password over an unsealed connection
       via  7ba61811592 s4:ldap_server: Update gMSA keys when DSDB_CONTROL_GMSA_UPDATE_OID control is specified
       via  24f109c59ff s4:dsdb:tests: Make use of ‘ldb’ parameter
       via  02d7ab13ee2 lib:crypto: Add more unit tests for GKDI functions
       via  b2d777a1ed2 s4:dsdb: Make use of DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS search flag
       via  118f3ba78fd s4:dsdb: Implement DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS search flag
       via  9149d1d338f s4:kdc: Correctly extract older NT hash
       via  c6fec5156fe tests/krb5: Note that lockout tests use password checks
       via  ed371ff0fa1 tests/krb5: Fix malapropism
       via  a916928acaf s4:kdc: Remove unnecessary cast
       via  8dca32eba2c pyglue: Remove unnecessary declaration
       via  460b1935b96 s4:kdc: Fix grammar
       via  faba757175f auth:credentials: Remove unnecessary declaration
       via  b6b8f9539b8 auth:credentials: Fix code spelling
       via  56dd910b837 python: Reformat code
       via  e25c6a21208 s4-gmsa: Do not attempt password set on remote LDAP connections
       via  977f5753fc8 s4:dsdb: Add dsdb_update_gmsa_keys()
       via  245dc1f0f2b s4:dsdb: Move the responsibility for determining whether an account is a gMSA out of gmsa_recalculate_managed_pwd()
       via  2f2d3b7cf28 s4:dsdb: Indicate to the LDAP server physical passwords that need to be refreshed
       via  99071bbcf4b s4:dsdb: Store found managed password ID as part of gMSA update structure
       via  8bcefaaa5c4 s4:dsdb: Store account DN as part of gMSA update structure
       via  6613aeca93a s4:dsdb: Only reuse the current password ID as the previous password ID when appropriate
       via  dcc5724ed75 s4:dsdb: Add a note that administrators should not set the clock too far in the future
       via  a397029813f s4:dsdb: No longer pass DSDB_SEARCH_ONE_ONLY flag to dsdb_search_dn()
       via  cdc63fa68d8 s4:dsdb: Explicitly return success error code
       via  1b765edbc95 tests/krb5: Add tests that gMSA keys are updated in the database when appropriate
       via  47c519af8e9 tests/krb5: Import MAX_CLOCK_SKEW more directly
       via  21d46f3ece3 tests/krb5: Extract method to unpack supplementalCredentials blob
       via  502070cd9a5 tests/krb5: Skip loop iteration if attribute has no values
       via  5eea17a71bd ldb: Check result of py_ldb_msg_keys()
      from  0159c48e897 ctdb-scripts: Do not de-duplicate the interfaces list

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


- Log -----------------------------------------------------------------
commit 20ce68f15940b3e8d4d53c10a71729b16cfb3908
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Fri Apr 19 14:16:03 2024 +1200

    tests/krb5: Test retrieving a denied gMSA password over an unsealed connection
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>
    
    Autobuild-User(master): Jo Sutton <jsutton at samba.org>
    Autobuild-Date(master): Sun Apr 21 23:17:53 UTC 2024 on atb-devel-224

commit 7ba6181159215e99d8a0f2f3974ee0d46d146f35
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Mon Apr 15 15:13:45 2024 +1200

    s4:ldap_server: Update gMSA keys when DSDB_CONTROL_GMSA_UPDATE_OID control is specified
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 24f109c59ff22a8a1f22ba4cdc118795e7b4d512
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Mon Apr 15 13:21:10 2024 +1200

    s4:dsdb:tests: Make use of ‘ldb’ parameter
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 02d7ab13ee271448efe5715bdaaf5e6907d32e08
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Mon Apr 15 12:19:12 2024 +1200

    lib:crypto: Add more unit tests for GKDI functions
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit b2d777a1ed23dfb968057411f43e92334f55705b
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Mon Apr 15 11:42:50 2024 +1200

    s4:dsdb: Make use of DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS search flag
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 118f3ba78fd1135fb7b254d1a2bb152eb5759923
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Tue Apr 9 16:24:43 2024 +1200

    s4:dsdb: Implement DSDB_SEARCH_UPDATE_MANAGED_PASSWORDS search flag
    
    View with ‘git show -b’.
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 9149d1d338f109f2220f6408418a6db6f3c43a11
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Thu Apr 11 17:17:54 2024 +1200

    s4:kdc: Correctly extract older NT hash
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit c6fec5156fe20da6a424d7239ee234aed0aa96c0
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Tue Apr 16 16:01:44 2024 +1200

    tests/krb5: Note that lockout tests use password checks
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit ed371ff0fa1bf3f67ad72ee206b67a693266f4b2
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Thu Apr 11 16:31:51 2024 +1200

    tests/krb5: Fix malapropism
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit a916928acaf91a2d238b7940b3f882db7d548564
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Wed Apr 10 12:01:09 2024 +1200

    s4:kdc: Remove unnecessary cast
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 8dca32eba2cc3b2947df029839d6962df971acc4
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Wed Apr 10 11:53:43 2024 +1200

    pyglue: Remove unnecessary declaration
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 460b1935b966f920cb117da6ca5a6ba9c48e7725
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Tue Apr 9 15:07:23 2024 +1200

    s4:kdc: Fix grammar
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit faba757175fdd56c6a489f76becde7e5f71694e3
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Tue Apr 9 14:31:11 2024 +1200

    auth:credentials: Remove unnecessary declaration
    
    This declaration is a hold‐over from the Python 2 module initialization
    pattern.
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit b6b8f9539b8fd91a1a9fdec6f181479759f04dd3
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Mon Apr 8 17:29:40 2024 +1200

    auth:credentials: Fix code spelling
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 56dd910b8372bee82e9cbe26b730dd4c938c9803
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Tue Mar 5 12:33:33 2024 +1300

    python: Reformat code
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit e25c6a212085f8c6ee7e99ed5cff68fde957e1db
Author: Andrew Bartlett <abartlet at samba.org>
Date:   Tue Mar 5 16:18:34 2024 +1300

    s4-gmsa: Do not attempt password set on remote LDAP connections
    
    Signed-off-by: Andrew Bartlett <abartlet at samba.org>
    Reviewed-by: Jo Sutton <josutton at catalyst.net.nz>

commit 977f5753fc866854e652918d5295718b347e7c3d
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Tue Feb 13 16:09:57 2024 +1300

    s4:dsdb: Add dsdb_update_gmsa_keys()
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 245dc1f0f2b10912dcba5502489acb0db13b830a
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Wed Apr 17 13:27:19 2024 +1200

    s4:dsdb: Move the responsibility for determining whether an account is a gMSA out of gmsa_recalculate_managed_pwd()
    
    and into its callers.
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 2f2d3b7cf284cc9f263060a36c3e4c58ca4a12bc
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Thu Apr 11 20:15:07 2024 +1200

    s4:dsdb: Indicate to the LDAP server physical passwords that need to be refreshed
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 99071bbcf4b8fa3718b4c1bc3f17bccb21f4f74c
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Tue Apr 16 14:03:36 2024 +1200

    s4:dsdb: Store found managed password ID as part of gMSA update structure
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 8bcefaaa5c4117ae4afc829f08ae239af7ebb00e
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Tue Apr 16 14:03:05 2024 +1200

    s4:dsdb: Store account DN as part of gMSA update structure
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 6613aeca93aba3e8edf96be4ceba0f349001b1dd
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Tue Apr 16 14:00:44 2024 +1200

    s4:dsdb: Only reuse the current password ID as the previous password ID when appropriate
    
    This should already be the case given the current logic, but let’s make
    it explicit.
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit dcc5724ed757ced4c84900ae4589425c5c384b1d
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Tue Apr 16 13:58:15 2024 +1200

    s4:dsdb: Add a note that administrators should not set the clock too far in the future
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit a397029813fe32c393c8dabbce8e210a0355811c
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Tue Apr 16 13:49:04 2024 +1200

    s4:dsdb: No longer pass DSDB_SEARCH_ONE_ONLY flag to dsdb_search_dn()
    
    As dsdb_search_dn() ignores this flag, passing it in doesn’t achieve
    anything.
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit cdc63fa68d81deb0ea1398b32adea5d4be0f6279
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Tue Apr 16 16:28:55 2024 +1200

    s4:dsdb: Explicitly return success error code
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 1b765edbc95b5cf9fead82c4e40af747123c6855
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Thu Apr 18 10:13:04 2024 +1200

    tests/krb5: Add tests that gMSA keys are updated in the database when appropriate
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 47c519af8e9cf57c3c1abd76c70eabbb8c7ba87c
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Fri Apr 19 12:59:52 2024 +1200

    tests/krb5: Import MAX_CLOCK_SKEW more directly
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 21d46f3ece3a48873b9a279b276868417f124fd1
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Fri Apr 19 12:58:36 2024 +1200

    tests/krb5: Extract method to unpack supplementalCredentials blob
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 502070cd9a5eb7eef0f98d73b967f1d63b9403f0
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Fri Apr 19 12:57:50 2024 +1200

    tests/krb5: Skip loop iteration if attribute has no values
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

commit 5eea17a71bd69f39226a32725a0b09b60dd5308c
Author: Jo Sutton <josutton at catalyst.net.nz>
Date:   Thu Apr 18 12:47:28 2024 +1200

    ldb: Check result of py_ldb_msg_keys()
    
    Passing NULL into PyObject_GetIter() can cause a segmentation fault.
    
    Signed-off-by: Jo Sutton <josutton at catalyst.net.nz>
    Reviewed-by: Andrew Bartlett <abartlet at samba.org>

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

Summary of changes:
 auth/credentials/pycredentials.c                  |   2 -
 auth/credentials/tests/bind.py                    |   2 +-
 lib/crypto/test_gkdi.c                            | 183 +++++++++++
 lib/ldb/pyldb.c                                   |   3 +
 python/pyglue.c                                   |   1 -
 python/samba/nt_time.py                           |  18 +-
 python/samba/tests/krb5/gmsa_tests.py             | 202 +++++++++++-
 python/samba/tests/krb5/kdc_base_test.py          |  52 +--
 python/samba/tests/krb5/lockout_tests.py          |   5 +-
 selftest/knownfail_mit_kdc_1_20                   |   1 +
 source3/passdb/pdb_samba_dsdb.c                   |   7 +-
 source4/auth/sam.c                                |  11 +-
 source4/dsdb/common/util.c                        | 149 ++++++---
 source4/dsdb/gmsa/gkdi.c                          |   2 +-
 source4/dsdb/gmsa/util.c                          | 378 +++++++++++++++++++++-
 source4/dsdb/gmsa/util.h                          |  25 ++
 source4/dsdb/samdb/ldb_modules/managed_pwd.c      |  23 ++
 source4/dsdb/samdb/ldb_modules/samba_dsdb.c       |   1 +
 source4/dsdb/tests/python/unicodepwd_encrypted.py |   4 +-
 source4/kdc/db-glue.c                             |  25 +-
 source4/kdc/kdc-heimdal.c                         |   3 +-
 source4/kdc/wscript_build                         |   2 +-
 source4/ldap_server/ldap_backend.c                |  44 +++
 source4/ntp_signd/ntp_signd.c                     |  28 +-
 source4/ntp_signd/wscript_build                   |   3 +-
 source4/rpc_server/netlogon/dcerpc_netlogon.c     |  90 ++++--
 source4/rpc_server/wscript_build                  |   1 +
 27 files changed, 1087 insertions(+), 178 deletions(-)


Changeset truncated at 500 lines:

diff --git a/auth/credentials/pycredentials.c b/auth/credentials/pycredentials.c
index 5cdbe7796e6..0bcb894f920 100644
--- a/auth/credentials/pycredentials.c
+++ b/auth/credentials/pycredentials.c
@@ -35,8 +35,6 @@
 #include "auth/kerberos/kerberos.h"
 #include "libcli/smb/smb_constants.h"
 
-void initcredentials(void);
-
 static PyObject *py_creds_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
 {
 	return pytalloc_steal(type, cli_credentials_init(NULL));
diff --git a/auth/credentials/tests/bind.py b/auth/credentials/tests/bind.py
index ce81b736e86..97370666b3b 100755
--- a/auth/credentials/tests/bind.py
+++ b/auth/credentials/tests/bind.py
@@ -140,7 +140,7 @@ unicodePwd:: """ + base64.b64encode(u"\"P at ssw0rd\"".encode('utf-16-le')).decode(
         res = ldb_virtual.search(base="", expression="", scope=SCOPE_BASE, attrs=["*"])
 
     def test_computer_account_bind(self):
-        # create a computer acocount for the test
+        # create a computer account for the test
         delete_force(self.ldb, self.computer_dn)
         self.ldb.add_ldif("""
 dn: """ + self.computer_dn + """
diff --git a/lib/crypto/test_gkdi.c b/lib/crypto/test_gkdi.c
index e6d3b28ae58..083d71eefd3 100644
--- a/lib/crypto/test_gkdi.c
+++ b/lib/crypto/test_gkdi.c
@@ -136,10 +136,193 @@ static void test_password_based_on_key_id(void **state)
 	talloc_free(mem_ctx);
 }
 
+static void assert_gkid_equal(const struct Gkid g1, const struct Gkid g2)
+{
+	assert_int_equal(g1.l0_idx, g2.l0_idx);
+	assert_int_equal(g1.l1_idx, g2.l1_idx);
+	assert_int_equal(g1.l2_idx, g2.l2_idx);
+}
+
+static void test_gkdi_rollover_interval(void **state)
+{
+	NTTIME interval;
+	bool ok;
+
+	ok = gkdi_rollover_interval(0, &interval);
+	assert_true(ok);
+	assert_int_equal(0, interval);
+
+	ok = gkdi_rollover_interval(1, &interval);
+	assert_true(ok);
+	assert_int_equal(UINT64_C(720000000000), interval);
+
+	ok = gkdi_rollover_interval(2, &interval);
+	assert_true(ok);
+	assert_int_equal(UINT64_C(1440000000000), interval);
+
+	ok = gkdi_rollover_interval(3, &interval);
+	assert_true(ok);
+	assert_int_equal(UINT64_C(2520000000000), interval);
+
+	ok = gkdi_rollover_interval(4, &interval);
+	assert_true(ok);
+	assert_int_equal(UINT64_C(3240000000000), interval);
+
+	ok = gkdi_rollover_interval(5, &interval);
+	assert_true(ok);
+	assert_int_equal(UINT64_C(4320000000000), interval);
+
+	ok = gkdi_rollover_interval(-1, &interval);
+	assert_false(ok);
+
+	ok = gkdi_rollover_interval(-2, &interval);
+	assert_false(ok);
+
+	ok = gkdi_rollover_interval(10675199, &interval);
+	assert_true(ok);
+	assert_int_equal(UINT64_C(9223371720000000000), interval);
+
+	ok = gkdi_rollover_interval(-10675198, &interval);
+	assert_false(ok);
+
+	ok = gkdi_rollover_interval(10675200, &interval);
+	assert_true(ok);
+	assert_int_equal(UINT64_C(9223372800000000000), interval);
+
+	ok = gkdi_rollover_interval(-10675199, &interval);
+	assert_false(ok);
+
+	ok = gkdi_rollover_interval(21350398, &interval);
+	/*
+	 * If we accepted this high of an interval, the result would be
+	 * 18446743800000000000.
+	 */
+	assert_false(ok);
+
+	ok = gkdi_rollover_interval(-21350397, &interval);
+	assert_false(ok);
+
+	ok = gkdi_rollover_interval(21350399, &interval);
+	assert_false(ok); /* too large to be represented */
+
+	ok = gkdi_rollover_interval(-21350398, &interval);
+	assert_false(ok); /* too small to be represented */
+}
+
+static void assert_get_interval_id(const NTTIME time,
+				   const struct Gkid expected_gkid)
+{
+	{
+		const bool valid = gkid_is_valid(expected_gkid);
+		assert_true(valid);
+	}
+
+	{
+		const struct Gkid interval_id = gkdi_get_interval_id(time);
+		assert_gkid_equal(expected_gkid, interval_id);
+	}
+}
+
+static void test_get_interval_id(void **state)
+{
+	assert_get_interval_id(0, Gkid(0, 0, 0));
+
+	assert_get_interval_id(gkdi_key_cycle_duration - 1, Gkid(0, 0, 0));
+
+	assert_get_interval_id(gkdi_key_cycle_duration, Gkid(0, 0, 1));
+
+	assert_get_interval_id(27 * gkdi_key_cycle_duration, Gkid(0, 0, 27));
+
+	assert_get_interval_id((gkdi_l2_key_iteration - 1) *
+				       gkdi_key_cycle_duration,
+			       Gkid(0, 0, gkdi_l2_key_iteration - 1));
+
+	assert_get_interval_id(gkdi_l2_key_iteration * gkdi_key_cycle_duration,
+			       Gkid(0, 1, 0));
+
+	assert_get_interval_id(17 * gkdi_l2_key_iteration *
+				       gkdi_key_cycle_duration,
+			       Gkid(0, 17, 0));
+
+	assert_get_interval_id(((gkdi_l1_key_iteration - 1) *
+					gkdi_l2_key_iteration +
+				3) * gkdi_key_cycle_duration,
+			       Gkid(0, gkdi_l1_key_iteration - 1, 3));
+
+	assert_get_interval_id(gkdi_l1_key_iteration * gkdi_l2_key_iteration *
+				       gkdi_key_cycle_duration,
+			       Gkid(1, 0, 0));
+
+	assert_get_interval_id(((1234 * gkdi_l1_key_iteration + 8) *
+					gkdi_l2_key_iteration +
+				13) * gkdi_key_cycle_duration,
+			       Gkid(1234, 8, 13));
+
+	assert_get_interval_id(INT64_MAX, Gkid(25019, 31, 29));
+
+	assert_get_interval_id(UINT64_MAX, Gkid(50039, 31, 27));
+}
+
+static void test_get_key_start_time(void **state)
+{
+	NTTIME start_time = 0;
+	bool ok;
+
+	/* Try passing an invalid GKID. */
+	ok = gkdi_get_key_start_time(invalid_gkid, &start_time);
+	assert_false(ok);
+
+	/* Try passing an L1 GKID rather than an L2 GKID. */
+	ok = gkdi_get_key_start_time(Gkid(0, 0, -1), &start_time);
+	assert_false(ok);
+
+	/* Test some L2 GKIDs. */
+
+	ok = gkdi_get_key_start_time(Gkid(0, 0, 0), &start_time);
+	assert_true(ok);
+	assert_int_equal(0, start_time);
+
+	ok = gkdi_get_key_start_time(Gkid(0, 0, 1), &start_time);
+	assert_true(ok);
+	assert_int_equal(gkdi_key_cycle_duration, start_time);
+
+	ok = gkdi_get_key_start_time(Gkid(123, 18, 2), &start_time);
+	assert_true(ok);
+	assert_int_equal(126530 * gkdi_key_cycle_duration, start_time);
+
+	ok = gkdi_get_key_start_time(Gkid(25019, 31, 29), &start_time);
+	assert_true(ok);
+	assert_int_equal(25620477 * gkdi_key_cycle_duration, start_time);
+
+	ok = gkdi_get_key_start_time(Gkid(25019, 31, 30), &start_time);
+	assert_true(ok);
+	assert_int_equal(UINT64_C(25620478) * gkdi_key_cycle_duration,
+			 start_time);
+
+	ok = gkdi_get_key_start_time(Gkid(50039, 31, 27), &start_time);
+	assert_true(ok);
+	assert_int_equal(UINT64_C(51240955) * gkdi_key_cycle_duration,
+			 start_time);
+
+	/*
+	 * Test GKIDs so high that their start times can’t be represented in
+	 * NTTIME.
+	 */
+
+	ok = gkdi_get_key_start_time(Gkid(50039, 31, 28), &start_time);
+	assert_false(ok);
+
+	ok = gkdi_get_key_start_time(Gkid(INT32_MAX, 31, 31), &start_time);
+	assert_false(ok);
+}
+
 int main(int argc, char *argv[])
 {
 	const struct CMUnitTest tests[] = {
 		cmocka_unit_test(test_password_based_on_key_id),
+		cmocka_unit_test(test_gkdi_rollover_interval),
+		cmocka_unit_test(test_get_interval_id),
+		cmocka_unit_test(test_get_key_start_time),
 	};
 
 	if (argc == 2) {
diff --git a/lib/ldb/pyldb.c b/lib/ldb/pyldb.c
index f416bfe6d5d..d54a952ac01 100644
--- a/lib/ldb/pyldb.c
+++ b/lib/ldb/pyldb.c
@@ -4143,6 +4143,9 @@ static PyObject *py_ldb_msg_iter(PyObject *self)
 	PyObject *list, *iter;
 
 	list = py_ldb_msg_keys(self, NULL);
+	if (list == NULL) {
+		return NULL;
+	}
 	iter = PyObject_GetIter(list);
 	Py_DECREF(list);
 	return iter;
diff --git a/python/pyglue.c b/python/pyglue.c
index c24d1b033a4..27cd41d5b9c 100644
--- a/python/pyglue.c
+++ b/python/pyglue.c
@@ -29,7 +29,6 @@
 #include "lib/cmdline/cmdline.h"
 #include "lib/crypto/gkdi.h"
 
-void init_glue(void);
 static PyObject *PyExc_NTSTATUSError;
 static PyObject *PyExc_WERRORError;
 static PyObject *PyExc_HRESULTError;
diff --git a/python/samba/nt_time.py b/python/samba/nt_time.py
index 098748f4f3c..714f9e97ef4 100644
--- a/python/samba/nt_time.py
+++ b/python/samba/nt_time.py
@@ -85,19 +85,21 @@ def nt_time_from_string(s: str) -> NtTime:
     UTC).
     """
     try:
-        if s == 'now':
+        if s == "now":
             dt = datetime.datetime.now(datetime.timezone.utc)
-        elif re.match(r'^\d{14}\.0Z$', s):
+        elif re.match(r"^\d{14}\.0Z$", s):
             # "20230127223641.0Z"
-            dt = datetime.datetime.strptime(s, '%Y%m%d%H%M%S.0Z')
+            dt = datetime.datetime.strptime(s, "%Y%m%d%H%M%S.0Z")
         else:
             dt = datetime.datetime.fromisoformat(s)
     except ValueError:
-        raise ValueError("Expected a date in either "
-                         "ISO8601 'YYYY-MM-DD HH:MM:SS' format, "
-                         "LDAP timestamp 'YYYYmmddHHMMSS.0Z', "
-                         "or the literal string 'now'. "
-                         f" Got '{s}'.")
+        raise ValueError(
+            "Expected a date in either "
+            "ISO8601 'YYYY-MM-DD HH:MM:SS' format, "
+            "LDAP timestamp 'YYYYmmddHHMMSS.0Z', "
+            "or the literal string 'now'. "
+            f" Got '{s}'."
+        )
 
     if dt.tzinfo is None:
         # This is a cursed timestamp with no timezone info. We have to
diff --git a/python/samba/tests/krb5/gmsa_tests.py b/python/samba/tests/krb5/gmsa_tests.py
index 1d3787af478..80529daf7d0 100755
--- a/python/samba/tests/krb5/gmsa_tests.py
+++ b/python/samba/tests/krb5/gmsa_tests.py
@@ -30,7 +30,7 @@ from itertools import chain
 
 import ldb
 
-from samba import auth, dsdb, gensec
+from samba import auth, dsdb, gensec, werror
 from samba.dcerpc import gkdi, gmsa, misc, netlogon, security
 from samba.ndr import ndr_pack, ndr_unpack
 from samba.nt_time import (
@@ -46,11 +46,12 @@ from samba.gkdi import (
     Gkid,
     GroupKey,
     KEY_CYCLE_DURATION,
+    MAX_CLOCK_SKEW,
 )
 
 from samba.tests import connect_samdb
 from samba.tests.krb5 import kcrypto
-from samba.tests.gkdi import GkdiBaseTest, MAX_CLOCK_SKEW
+from samba.tests.gkdi import GkdiBaseTest, ROOT_KEY_START_TIME
 from samba.tests.krb5.kdc_base_test import KDCBaseTest
 from samba.tests.krb5.raw_testcase import KerberosCredentials
 from samba.tests.krb5.rfc4120_constants import (
@@ -632,9 +633,17 @@ class GmsaTests(GkdiBaseTest, KDCBaseTest):
         )
 
     def check_managed_password_access(
-        self, creds: Credentials, *, expect_access
+        self,
+        creds: Credentials,
+        *,
+        samdb: Optional[SamDB] = None,
+        expect_access: bool = False,
+        expected_werror: int = werror.WERR_SUCCESS,
     ) -> None:
-        samdb = self.get_samdb()
+        if samdb is None:
+            samdb = self.get_samdb()
+        if expected_werror:
+            self.assertFalse(expect_access)
         managed_service_accounts_dn = self.get_managed_service_accounts_dn()
         username = creds.get_username()
 
@@ -648,12 +657,24 @@ class GmsaTests(GkdiBaseTest, KDCBaseTest):
         for dn, scope in searches:
             # Perform a search and see whether we’re allowed to view the managed password.
 
-            res = samdb.search(
-                dn,
-                scope=scope,
-                expression=f"sAMAccountName={username}",
-                attrs=["msDS-ManagedPassword"],
-            )
+            try:
+                res = samdb.search(
+                    dn,
+                    scope=scope,
+                    expression=f"sAMAccountName={username}",
+                    attrs=["msDS-ManagedPassword"],
+                )
+            except ldb.LdbError as err:
+                self.assertTrue(expected_werror, "got an unexpected error")
+
+                num, estr = err.args
+                if num != ldb.ERR_OPERATIONS_ERROR:
+                    raise
+
+                self.assertIn(f"{expected_werror:08X}", estr)
+                return
+
+            self.assertFalse(expected_werror, "expected to get an error")
             self.assertEqual(1, len(res), "should always find the gMSA")
 
             managed_password = res[0].get("msDS-ManagedPassword", idx=0)
@@ -677,6 +698,36 @@ class GmsaTests(GkdiBaseTest, KDCBaseTest):
             self.gmsa_account(msa_membership=deny_world_sddl), expect_access=False
         )
 
+    def test_retrieving_denied_password_over_unsealed_connection(self):
+        # Requires --use-kerberos=required, or it automatically upgrades to an
+        # encrypted connection.
+
+        # Remove FEATURE_SEAL which gets added by insta_creds.
+        creds = self.insta_creds(template=self.get_admin_creds())
+        creds.set_gensec_features(creds.get_gensec_features() & ~gensec.FEATURE_SEAL)
+
+        lp = self.get_lp()
+
+        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.
+        samdb = SamDB(
+            f"ldap://{self.dc_host}",
+            credentials=creds,
+            session_info=auth.system_session(lp),
+            lp=lp,
+        )
+
+        # Deny anyone from being able to view the password.
+        deny_world_sddl = "O:SYD:(D;;RP;;;WD)"
+        self.check_managed_password_access(
+            self.gmsa_account(msa_membership=deny_world_sddl),
+            samdb=samdb,
+            expected_werror=werror.WERR_DS_CONFIDENTIALITY_REQUIRED,
+        )
+
     def future_gkid(self) -> Gkid:
         """Return (6333, 26, 5)—an arbitrary GKID far enough in the future that
         it’s situated beyond any reasonable rollover period. But not so far in
@@ -839,6 +890,137 @@ class GmsaTests(GkdiBaseTest, KDCBaseTest):
         )
         self.check_managed_pwd(samdb, creds, expected_managed_pwd=expected)
 
+    def test_retrieving_managed_password_triggers_keys_update(self):
+        # Create a root key with a start time early enough to be usable at the
+        # time the gMSA is purported to be created.
+        samdb = self.get_samdb()
+        domain_dn = self.get_server_dn(samdb)
+        self.create_root_key(samdb, domain_dn, use_start_time=ROOT_KEY_START_TIME)
+
+        password_interval = 16
+
+        local_samdb = self.get_local_samdb()
+        series = GmsaSeries(Gkid(100, 0, 0), gkdi_rollover_interval(password_interval))
+        self.set_db_time(local_samdb, series.start_of_interval(0))
+
+        creds = self.gmsa_account(samdb=local_samdb, interval=password_interval)
+        dn = creds.get_dn()
+
+        current_nt_time = self.current_nt_time(local_samdb)
+        self.set_db_time(local_samdb, current_nt_time)
+
+        # Search the local database for the account’s keys.
+        res = local_samdb.search(
+            dn, scope=ldb.SCOPE_BASE, attrs=["unicodePwd", "supplementalCredentials"]
+        )
+        self.assertEqual(1, len(res))
+
+        previous_nt_hash = res[0].get("unicodePwd", idx=0)
+        previous_supplemental_creds = self.unpack_supplemental_credentials(
+            res[0].get("supplementalCredentials", idx=0)
+        )
+
+        # Search for the managed password over LDAP, triggering an update of the
+        # keys in the database.
+        res = samdb.search(dn, scope=ldb.SCOPE_BASE, attrs=["msDS-ManagedPassword"])
+        self.assertEqual(1, len(res))
+
+        # Verify that the password is present in the result.
+        managed_password = res[0].get("msDS-ManagedPassword", idx=0)
+        self.assertIsNotNone(managed_password, "should be allowed to view the password")
+
+        # Search the local database again for the account’s keys, which should
+        # have been updated.
+        res = local_samdb.search(
+            dn, scope=ldb.SCOPE_BASE, attrs=["unicodePwd", "supplementalCredentials"]
+        )
+        self.assertEqual(1, len(res))
+
+        nt_hash = res[0].get("unicodePwd", idx=0)
+        supplemental_creds = self.unpack_supplemental_credentials(
+            res[0].get("supplementalCredentials", idx=0)
+        )
+
+        self.assertNotEqual(
+            previous_nt_hash, nt_hash, "NT hash has not been updated (yet)"
+        )
+        self.assertNotEqual(
+            previous_supplemental_creds,
+            supplemental_creds,
+            "supplementalCredentials has not been updated (yet)",
+        )
+
+    def test_authentication_triggers_keys_update(self):
+        # Create a root key with a start time early enough to be usable at the
+        # time the gMSA is purported to be created. But don’t create it on a
+        # local samdb with a specifically set time, because (if the key isn’t
+        # deleted later) we could end up with multiple keys with identical
+        # creation and start times, and tests failing when the test and the
+        # server don’t agree on which root key to use at a specific time.
+        samdb = self.get_samdb()
+        domain_dn = self.get_server_dn(samdb)
+        self.create_root_key(samdb, domain_dn, use_start_time=ROOT_KEY_START_TIME)
+
+        password_interval = 16
+
+        local_samdb = self.get_local_samdb()
+        series = GmsaSeries(Gkid(100, 0, 0), gkdi_rollover_interval(password_interval))
+        self.set_db_time(local_samdb, series.start_of_interval(0))
+
+        creds = self.gmsa_account(samdb=local_samdb, interval=password_interval)
+        dn = creds.get_dn()
+
+        current_nt_time = self.current_nt_time(local_samdb)
+        self.set_db_time(local_samdb, current_nt_time)
+
+        # Search the local database for the account’s keys.
+        res = local_samdb.search(
+            dn, scope=ldb.SCOPE_BASE, attrs=["unicodePwd", "supplementalCredentials"]
+        )
+        self.assertEqual(1, len(res))
+
+        previous_nt_hash = res[0].get("unicodePwd", idx=0)
+        previous_supplemental_creds = self.unpack_supplemental_credentials(
+            res[0].get("supplementalCredentials", idx=0)
+        )
+
+        # Calculate the password with which to authenticate.
+        managed_pwd = self.expected_current_gmsa_password_blob(
+            samdb, creds, future_key_is_acceptable=False


-- 
Samba Shared Repository



More information about the samba-cvs mailing list