The branch, v4-21-test has been updated
       via  d1a778414e1 s3:net: Pass down the server from cmdline to 
sync_pw2keytabs()
       via  015c3ef6c10 tests: Add test for 'net ads join' to a preferred DC
       via  393e35dca2b selftest: Add the short name for localvampiredc to 
hosts file
      from  070ff8f5766 VERSION: Bump version up to Samba 4.21.9...

https://git.samba.org/?p=samba.git;a=shortlog;h=v4-21-test


- Log -----------------------------------------------------------------
commit d1a778414e178bb1a3495d391d6f55ab883e8903
Author: Andreas Schneider <[email protected]>
Date:   Mon Jul 28 10:43:36 2025 +0200

    s3:net: Pass down the server from cmdline to sync_pw2keytabs()
    
    This makes sure that during 'net ads join' the keytab create code
    - sync_pw2keytabs() talks to the same DC at what the machine account
    was created.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15905
    
    Signed-off-by: Andreas Schneider <[email protected]>
    Signed-off-by: Pavel Filipenský <[email protected]>
    Pair-Programmed-With: Pavel Filipenský <[email protected]>
    
    Reviewed-by: Alexander Bokovoy <[email protected]>
    
    Autobuild-User(master): Pavel Filipensky <[email protected]>
    Autobuild-Date(master): Fri Sep  5 13:38:33 UTC 2025 on atb-devel-224
    
    (cherry picked from commit 5d1d3a8b568b5a07ed1ed537d20aa93820cecc14)
    
    Autobuild-User(v4-21-test): Jule Anger <[email protected]>
    Autobuild-Date(v4-21-test): Thu Sep 11 12:28:03 UTC 2025 on atb-devel-224

commit 015c3ef6c1040ca64d29ae95ade7cc8970929b25
Author: Pavel Filipenský <[email protected]>
Date:   Mon Aug 4 11:20:54 2025 +0200

    tests: Add test for 'net ads join' to a preferred DC
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15905
    
    Signed-off-by: Pavel Filipenský <[email protected]>
    Reviewed-by: Alexander Bokovoy <[email protected]>
    (backported from commit 36f6ac547c09f492d1dcab11570e8bcbd377cf26)

commit 393e35dca2bb83492c879c9d044f2505daef377b
Author: Pavel Filipenský <[email protected]>
Date:   Tue Jul 29 11:19:07 2025 +0200

    selftest: Add the short name for localvampiredc to hosts file
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15905
    
    Signed-off-by: Pavel Filipenský <[email protected]>
    Reviewed-by: Alexander Bokovoy <[email protected]>
    (cherry picked from commit 5d2f60ae5aa96751b74901ae5384291ef338b152)

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

Summary of changes:
 selftest/target/Samba4.pm                          |  2 +-
 source3/include/secrets.h                          | 25 +++++----
 source3/libads/ads_proto.h                         |  2 +-
 source3/libads/kerberos_keytab.c                   | 24 +++++++--
 source3/libads/trusts_util.c                       | 15 +++---
 source3/libads/util.c                              | 10 ++--
 source3/libnet/libnet_join.c                       |  2 +-
 source3/passdb/machine_account_secrets.c           | 10 ++--
 source3/utils/net.c                                | 10 ++--
 source3/utils/net_ads.c                            |  2 +-
 source4/selftest/tests.py                          |  1 +
 .../blackbox/test_net_ads_join_to_preferred_dc.sh  | 61 ++++++++++++++++++++++
 12 files changed, 129 insertions(+), 35 deletions(-)
 create mode 100755 testprogs/blackbox/test_net_ads_join_to_preferred_dc.sh


Changeset truncated at 500 lines:

diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
index e917f65fc36..1a86f7a07d5 100755
--- a/selftest/target/Samba4.pm
+++ b/selftest/target/Samba4.pm
@@ -869,7 +869,7 @@ nogroup:x:65534:nobody
 
        my $hostname = lc($ctx->{hostname});
        open(HOSTS, ">>$ctx->{nsswrap_hosts}");
-       if ($hostname eq "localdc") {
+       if ($hostname eq "localdc" || $hostname eq "localvampiredc") {
                print HOSTS "$ctx->{ipv4} ${hostname}.$ctx->{dnsname} 
$ctx->{dnsname} ${hostname}\n";
                print HOSTS "$ctx->{ipv6} ${hostname}.$ctx->{dnsname} 
$ctx->{dnsname} ${hostname}\n";
        } else {
diff --git a/source3/include/secrets.h b/source3/include/secrets.h
index a454c8bb8ff..061b9c6ef34 100644
--- a/source3/include/secrets.h
+++ b/source3/include/secrets.h
@@ -125,12 +125,15 @@ char *secrets_domain_info_string(TALLOC_CTX *mem_ctx, 
const struct secrets_domai
 NTSTATUS secrets_fetch_or_upgrade_domain_info(const char *domain,
                                        TALLOC_CTX *mem_ctx,
                                        struct secrets_domain_info1 **pinfo);
-NTSTATUS secrets_prepare_password_change(const char *domain, const char 
*dcname,
-                                        const char *cleartext_unix,
-                                        TALLOC_CTX *mem_ctx,
-                                        struct secrets_domain_info1 **pinfo,
-                                        struct secrets_domain_info1_change 
**pprev,
-                                        NTSTATUS (*sync_pw2keytabs_fn)(void));
+NTSTATUS secrets_prepare_password_change(
+       const char *domain,
+       const char *dcname,
+       const char *cleartext_unix,
+       TALLOC_CTX *mem_ctx,
+       struct secrets_domain_info1 **pinfo,
+       struct secrets_domain_info1_change **pprev,
+       NTSTATUS (*sync_pw2keytabs_fn)(const char *),
+       const char *opt_host);
 NTSTATUS secrets_failed_password_change(const char *change_server,
                                        NTSTATUS local_status,
                                        NTSTATUS remote_status,
@@ -139,10 +142,12 @@ NTSTATUS secrets_defer_password_change(const char 
*change_server,
                                       NTSTATUS local_status,
                                       NTSTATUS remote_status,
                                       const struct secrets_domain_info1 *info);
-NTSTATUS secrets_finish_password_change(const char *change_server,
-                                       NTTIME change_time,
-                                       const struct secrets_domain_info1 *info,
-                                       NTSTATUS (*sync_pw2keytabs_fn)(void));
+NTSTATUS secrets_finish_password_change(
+       const char *change_server,
+       NTTIME change_time,
+       const struct secrets_domain_info1 *info,
+       NTSTATUS (*sync_pw2keytabs_fn)(const char *),
+       const char *prefer_dc);
 bool secrets_delete_machine_password_ex(const char *domain, const char *realm);
 bool secrets_delete_domain_sid(const char *domain);
 char *secrets_fetch_prev_machine_password(const char *domain);
diff --git a/source3/libads/ads_proto.h b/source3/libads/ads_proto.h
index 8440c35e46d..2e67eef155c 100644
--- a/source3/libads/ads_proto.h
+++ b/source3/libads/ads_proto.h
@@ -230,6 +230,6 @@ struct spn_struct {
 /* parse a windows style SPN, returns NULL if parsing fails */
 struct spn_struct *parse_spn(TALLOC_CTX *ctx, const char *srvprinc);
 
-NTSTATUS sync_pw2keytabs(void);
+NTSTATUS sync_pw2keytabs(const char *prefer_dc);
 
 #endif /* _LIBADS_ADS_PROTO_H_ */
diff --git a/source3/libads/kerberos_keytab.c b/source3/libads/kerberos_keytab.c
index 49a892e5a55..1d4f9c12e1d 100644
--- a/source3/libads/kerberos_keytab.c
+++ b/source3/libads/kerberos_keytab.c
@@ -84,6 +84,7 @@ struct pw2kt_global_state {
        char *ad_upn;
        char *ad_sam_account;
        char **ad_spn_array;
+       const char *prefer_dc;
        size_t ad_num_spns;
        /* This is from secrets.db */
        struct secrets_domain_info1 *info;
@@ -869,8 +870,11 @@ static ADS_STATUS pw2kt_get_dc_info(struct 
pw2kt_global_state *state)
        int count;
        bool ok;
        TALLOC_CTX *tmp_ctx = talloc_stackframe();
-       ADS_STRUCT *ads = ads_init(
-               tmp_ctx, lp_realm(), lp_workgroup(), NULL, ADS_SASL_SIGN);
+       ADS_STRUCT *ads = ads_init(tmp_ctx,
+                                  lp_realm(),
+                                  lp_workgroup(),
+                                  state->prefer_dc,
+                                  ADS_SASL_SIGN);
 
        if (ads == NULL) {
                DBG_ERR("ads_init() failed\n");
@@ -1029,7 +1033,20 @@ static bool pw2kt_default_keytab_name(char *name_str, 
size_t name_size)
        return true;
 }
 
-NTSTATUS sync_pw2keytabs(void)
+/**
+ * @internal
+ *
+ * @brief Sync machine password from secrets to keytab
+ *
+ * @param prefer_dc  The DC we should talk to. This is especially important
+ *                   during domain join. Pass NULL if we should pick a random
+ *                   one.
+ *
+ * @return An NTSTATUS error code.
+ *
+ * @see NT_STATUS_IS_OK()
+ */
+NTSTATUS sync_pw2keytabs(const char *prefer_dc)
 {
        TALLOC_CTX *frame = talloc_stackframe();
        const struct loadparm_substitution *lp_sub =
@@ -1055,6 +1072,7 @@ NTSTATUS sync_pw2keytabs(void)
                TALLOC_FREE(frame);
                return NT_STATUS_NO_MEMORY;
        }
+       state->prefer_dc = prefer_dc;
 
        lp_ptr = lp_sync_machine_password_to_keytab();
        if (lp_ptr == NULL) {
diff --git a/source3/libads/trusts_util.c b/source3/libads/trusts_util.c
index 6f805f2365e..e774a0b73e6 100644
--- a/source3/libads/trusts_util.c
+++ b/source3/libads/trusts_util.c
@@ -325,10 +325,11 @@ NTSTATUS trust_pw_change(struct 
netlogon_creds_cli_context *context,
                                                         &info,
                                                         &prev,
 #ifdef HAVE_ADS
-                                                        sync_pw2keytabs);
+                                                        sync_pw2keytabs,
 #else
-                                                        NULL);
+                                                        NULL,
 #endif
+                                                        NULL /* opt_host */);
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(0, ("secrets_prepare_password_change() failed for 
domain %s!\n",
                                  domain));
@@ -429,10 +430,11 @@ NTSTATUS trust_pw_change(struct 
netlogon_creds_cli_context *context,
                        prev->password->change_time,
                        info,
 #ifdef HAVE_ADS
-                       sync_pw2keytabs);
+                       sync_pw2keytabs,
 #else
-                       NULL);
+                       NULL,
 #endif
+                       prev->password->change_server);
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(0, ("secrets_prepare_password_change() failed for 
domain %s!\n",
                                  domain));
@@ -578,10 +580,11 @@ NTSTATUS trust_pw_change(struct 
netlogon_creds_cli_context *context,
                        info->next_change->change_time,
                        info,
 #ifdef HAVE_ADS
-                       sync_pw2keytabs);
+                       sync_pw2keytabs,
 #else
-                       NULL);
+                       NULL,
 #endif
+                       info->next_change->change_server);
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(0, ("secrets_finish_password_change() failed for 
domain %s!\n",
                                  domain));
diff --git a/source3/libads/util.c b/source3/libads/util.c
index 243dd09f3d0..360e556ab9b 100644
--- a/source3/libads/util.c
+++ b/source3/libads/util.c
@@ -59,10 +59,11 @@ ADS_STATUS ads_change_trust_account_password(ADS_STRUCT 
*ads, char *host_princip
                                                 &info,
                                                 &prev,
 #ifdef HAVE_ADS
-                                                sync_pw2keytabs);
+                                                sync_pw2keytabs,
 #else
-                                                NULL);
+                                                NULL,
 #endif
+                                                ads->auth.kdc_server);
        if (!NT_STATUS_IS_OK(status)) {
                return ADS_ERROR_NT(status);
        }
@@ -138,10 +139,11 @@ ADS_STATUS ads_change_trust_account_password(ADS_STRUCT 
*ads, char *host_princip
                                                now,
                                                info,
 #ifdef HAVE_ADS
-                                               sync_pw2keytabs);
+                                               sync_pw2keytabs,
 #else
-                                               NULL);
+                                               NULL,
 #endif
+                                               ads->auth.kdc_server);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(1,("Failed to save machine password\n"));
                return ADS_ERROR_NT(status);
diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c
index dc924ad199a..d47178e5700 100644
--- a/source3/libnet/libnet_join.c
+++ b/source3/libnet/libnet_join.c
@@ -866,7 +866,7 @@ static ADS_STATUS libnet_join_set_etypes(TALLOC_CTX 
*mem_ctx,
 static bool libnet_join_create_keytab(TALLOC_CTX *mem_ctx,
                                      struct libnet_JoinCtx *r)
 {
-       NTSTATUS ntstatus = sync_pw2keytabs();
+       NTSTATUS ntstatus = sync_pw2keytabs(r->in.dc_name);
 
        return NT_STATUS_IS_OK(ntstatus);
 }
diff --git a/source3/passdb/machine_account_secrets.c 
b/source3/passdb/machine_account_secrets.c
index 21571349004..8e16b2c5640 100644
--- a/source3/passdb/machine_account_secrets.c
+++ b/source3/passdb/machine_account_secrets.c
@@ -1674,7 +1674,8 @@ NTSTATUS secrets_prepare_password_change(const char 
*domain, const char *dcname,
                                         TALLOC_CTX *mem_ctx,
                                         struct secrets_domain_info1 **pinfo,
                                         struct secrets_domain_info1_change 
**pprev,
-                                        NTSTATUS (*sync_pw2keytabs_fn)(void))
+                                        NTSTATUS (*sync_pw2keytabs_fn)(const 
char *),
+                                        const char *opt_host)
 {
        TALLOC_CTX *frame = talloc_stackframe();
        struct db_context *db = NULL;
@@ -1770,7 +1771,7 @@ NTSTATUS secrets_prepare_password_change(const char 
*domain, const char *dcname,
        }
 
        if (prev == NULL && sync_pw2keytabs_fn != NULL) {
-               status = sync_pw2keytabs_fn();
+               status = sync_pw2keytabs_fn(opt_host);
                if (!NT_STATUS_IS_OK(status)) {
                        DBG_ERR("Sync of machine password failed.\n");
                        dbwrap_transaction_cancel(db);
@@ -2023,7 +2024,8 @@ NTSTATUS secrets_defer_password_change(const char 
*change_server,
 NTSTATUS secrets_finish_password_change(const char *change_server,
                                        NTTIME change_time,
                                        const struct secrets_domain_info1 
*cookie,
-                                       NTSTATUS (*sync_pw2keytabs_fn)(void))
+                                       NTSTATUS (*sync_pw2keytabs_fn)(const 
char *),
+                                       const char *prefer_dc)
 {
        const char *domain = cookie->domain_info.name.string;
        TALLOC_CTX *frame = talloc_stackframe();
@@ -2102,7 +2104,7 @@ NTSTATUS secrets_finish_password_change(const char 
*change_server,
        }
 
        if (sync_pw2keytabs_fn != NULL) {
-               status = sync_pw2keytabs_fn();
+               status = sync_pw2keytabs_fn(prefer_dc);
                if (!NT_STATUS_IS_OK(status)) {
                        DBG_ERR("Sync of machine password failed.\n");
                        TALLOC_FREE(frame);
diff --git a/source3/utils/net.c b/source3/utils/net.c
index 7ce93ced79e..ecabd980d0c 100644
--- a/source3/utils/net.c
+++ b/source3/utils/net.c
@@ -235,10 +235,11 @@ static int net_changesecretpw(struct net_context *c, int 
argc,
                                                         &info,
                                                         &prev,
 #ifdef HAVE_ADS
-                                                        sync_pw2keytabs);
+                                                        sync_pw2keytabs,
 #else
-                                                        NULL);
+                                                        NULL,
 #endif
+                                                        c->opt_host);
                if (!NT_STATUS_IS_OK(status)) {
                        d_fprintf(stderr,
                                _("Unable to write the machine account password 
in the secrets database"));
@@ -261,10 +262,11 @@ static int net_changesecretpw(struct net_context *c, int 
argc,
                                                        now,
                                                        info,
 #ifdef HAVE_ADS
-                                                       sync_pw2keytabs);
+                                                       sync_pw2keytabs,
 #else
-                                                       NULL);
+                                                       NULL,
 #endif
+                                                       c->opt_host);
                if (!NT_STATUS_IS_OK(status)) {
                        d_fprintf(stderr,
                                _("Unable to write the machine account password 
in the secrets database"));
diff --git a/source3/utils/net_ads.c b/source3/utils/net_ads.c
index 4778c6d9cf4..c68b45aacd1 100644
--- a/source3/utils/net_ads.c
+++ b/source3/utils/net_ads.c
@@ -2959,7 +2959,7 @@ static int net_ads_keytab_create(struct net_context *c, 
int argc, const char **a
                net_use_krb_machine_account(c);
        }
 
-       ntstatus = sync_pw2keytabs();
+       ntstatus = sync_pw2keytabs(c->opt_host);
        ret = NT_STATUS_IS_OK(ntstatus) ? 0 : 1;
        return ret;
 }
diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index 3b046c27a28..a77a3d8f780 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -897,6 +897,7 @@ plantestsuite("samba4.blackbox.rfc2307_mapping",
 plantestsuite("samba4.blackbox.chgdcpass", "chgdcpass", [os.path.join(bbdir, 
"test_chgdcpass.sh"), '$SERVER', r"CHGDCPASS\$", '$REALM', '$DOMAIN', 
'$PREFIX/chgdcpass', "aes256-cts-hmac-sha1-96", '$PREFIX/chgdcpass', 
smbclient3])
 plantestsuite("samba4.blackbox.samba_upgradedns(chgdcpass:local)", 
"chgdcpass:local", [os.path.join(bbdir, "test_samba_upgradedns.sh"), '$SERVER', 
'$REALM', '$PREFIX', '$SELFTEST_PREFIX/chgdcpass'])
 plantestsuite("samba4.blackbox.net_ads", "ad_dc:client", [os.path.join(bbdir, 
"test_net_ads.sh"), '$DC_SERVER', '$DC_USERNAME', '$DC_PASSWORD', 
'$PREFIX_ABS'])
+plantestsuite("samba4.blackbox.net_ads_join", "vampire_dc:client", 
[os.path.join(bbdir, "test_net_ads_join_to_preferred_dc.sh"), '$DC_SERVER', 
'$DC_USERNAME', '$DC_PASSWORD', '$PREFIX'])
 plantestsuite("samba4.blackbox.net_offlinejoin", "ad_dc:client", 
[os.path.join(bbdir, "test_net_offline.sh"), '$DC_SERVER', '$DC_USERNAME', 
'$DC_PASSWORD', '$PREFIX_ABS'])
 plantestsuite("samba4.blackbox.client_etypes_all(ad_dc:client)", 
"ad_dc:client", [os.path.join(bbdir, "test_client_etypes.sh"), '$DC_SERVER', 
'$DC_USERNAME', '$DC_PASSWORD', '$PREFIX_ABS', 'all', '17_18_23'])
 plantestsuite("samba4.blackbox.client_etypes_legacy(ad_dc:client)", 
"ad_dc:client", [os.path.join(bbdir, "test_client_etypes.sh"), '$DC_SERVER', 
'$DC_USERNAME', '$DC_PASSWORD', '$PREFIX_ABS', 'legacy', '23'])
diff --git a/testprogs/blackbox/test_net_ads_join_to_preferred_dc.sh 
b/testprogs/blackbox/test_net_ads_join_to_preferred_dc.sh
new file mode 100755
index 00000000000..1bebc2f4dbe
--- /dev/null
+++ b/testprogs/blackbox/test_net_ads_join_to_preferred_dc.sh
@@ -0,0 +1,61 @@
+if [ $# -lt 4 ]; then
+       cat <<EOF
+Usage: test_net_ads.sh DC_SERVER DC_USERNAME DC_PASSWORD BASEDIR
+EOF
+       exit 1
+fi
+
+DC_SERVER=$1
+DC_USERNAME=$2
+DC_PASSWORD=$3
+BASEDIR=$4
+
+HOSTNAME=$(dd if=/dev/urandom bs=1 count=32 2>/dev/null | sha1sum | cut -b 
1-10)
+
+RUNDIR=$(pwd)
+cd $BASEDIR
+WORKDIR=$(mktemp -d -p .)
+WORKDIR=$(basename $WORKDIR)
+cp -a client/* $WORKDIR/
+sed -ri "s@(dir|directory) = (.*)/client/@\1 = \2/$WORKDIR/@" 
$WORKDIR/client.conf
+sed -ri "s/netbios name = .*/netbios name = $HOSTNAME/" $WORKDIR/client.conf
+rm -f $WORKDIR/private/secrets.tdb
+cd $RUNDIR
+
+failed=0
+
+net_tool="$BINDIR/net --configfile=$BASEDIR/$WORKDIR/client.conf 
--option=security=ads"
+
+# Load test functions
+. $(dirname $0)/subunit.sh
+. "$(dirname "${0}")/common_test_fns.inc"
+
+# This test is run in environment with two DCs ('localdc' and 'localvampiredc')
+# The 'net ads join' has these two steps:
+#   1. create machine account at DC ('-S' points to 'localvampiredc')
+#   2. create keytab and sync the KVNO from a DC
+#
+# It must be ensured that in step #2 the keytab code contacts the same DC
+# ('localvampiredc'). The configuration below tries to break it.
+# We disable [SAF/DOMAIN/...] and [SAFJOIN/DOMAIN/...] by setting TTL to '-1'
+# And via setting 'password server' to 'localdc' we manage that
+# get_dc_list() returns 'localdc' instead of 'localvampiredc'
+#
+# As long as the keytab code is not explicitly told to use the same DC as join,
+# we get failure:
+# gensec_gse_client_prepare_ccache: Kinit for [email protected] to 
access ldap/localdc.samba.example.com failed: Client not found in Kerberos 
database: NT_STATUS_LOGON_FAILURE
+
+cat <<EOF >>$BASEDIR/$WORKDIR/client.conf
+sync machine password to keytab = 
$BASEDIR/keytab:account_name:machine_password:sync_kvno
+password server = $DC_SERVER
+saf: join ttl = -1
+saf: ttl = -1
+EOF
+
+testit "join" $VALGRIND $net_tool ads join -S$SERVER 
-U$DC_USERNAME%$DC_PASSWORD || failed=$(expr $failed + 1)
+
+testit "leave" $VALGRIND $net_tool ads leave -U$DC_USERNAME%$DC_PASSWORD || 
failed=$(expr $failed + 1)
+
+rm -rf $BASEDIR/$WORKDIR
+
+exit $failed


-- 
Samba Shared Repository

Reply via email to