From eb1d1f19a23807c9951dd178b93f3cfd94f68146 Mon Sep 17 00:00:00 2001
From: Ralph Boehme <slow@samba.org>
Date: Tue, 22 Nov 2022 16:09:34 +0100
Subject: [PATCH] winbindd: add dcname arg to ChangeMachineAccount request

Existing callers will pass an empty string, later a new caller will pass an
explicit DC name taken from the wbinfo command line.

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
---
 librpc/idl/winbind.idl                        |  1 +
 .../winbindd/winbindd_change_machine_acct.c   |  8 +++++-
 source3/winbindd/winbindd_dual_srv.c          | 26 ++++++++++++++++---
 3 files changed, 31 insertions(+), 4 deletions(-)

diff --git a/librpc/idl/winbind.idl b/librpc/idl/winbind.idl
index 2adfc853835..de8fbc75c23 100644
--- a/librpc/idl/winbind.idl
+++ b/librpc/idl/winbind.idl
@@ -162,6 +162,7 @@ interface winbind
 	);
 
     NTSTATUS wbint_ChangeMachineAccount(
+		[in,unique,string,charset(UTF8)] char *dcname
 	);
 
     NTSTATUS wbint_PingDc(
diff --git a/source3/winbindd/winbindd_change_machine_acct.c b/source3/winbindd/winbindd_change_machine_acct.c
index 83eb99ba64a..fe5b9bf8a92 100644
--- a/source3/winbindd/winbindd_change_machine_acct.c
+++ b/source3/winbindd/winbindd_change_machine_acct.c
@@ -36,6 +36,7 @@ struct tevent_req *winbindd_change_machine_acct_send(TALLOC_CTX *mem_ctx,
 	struct tevent_req *req, *subreq;
 	struct winbindd_change_machine_acct_state *state;
 	struct winbindd_domain *domain;
+	const char *dcname = NULL;
 
 	req = tevent_req_create(mem_ctx, &state,
 				struct winbindd_change_machine_acct_state);
@@ -43,6 +44,10 @@ struct tevent_req *winbindd_change_machine_acct_send(TALLOC_CTX *mem_ctx,
 		return NULL;
 	}
 
+	if (request->data.init_conn.dcname[0] != '\0') {
+		dcname = request->data.init_conn.dcname;
+	}
+
 	domain = find_domain_from_name(request->domain_name);
 	if (domain == NULL) {
 		tevent_req_nterror(req, NT_STATUS_NO_SUCH_DOMAIN);
@@ -62,7 +67,8 @@ struct tevent_req *winbindd_change_machine_acct_send(TALLOC_CTX *mem_ctx,
 	}
 
 	subreq = dcerpc_wbint_ChangeMachineAccount_send(state, ev,
-							dom_child_handle(domain));
+							dom_child_handle(domain),
+							dcname);
 	if (tevent_req_nomem(subreq, req)) {
 		return tevent_req_post(req, ev);
 	}
diff --git a/source3/winbindd/winbindd_dual_srv.c b/source3/winbindd/winbindd_dual_srv.c
index ffa4ad95b29..aaa78a9cb6e 100644
--- a/source3/winbindd/winbindd_dual_srv.c
+++ b/source3/winbindd/winbindd_dual_srv.c
@@ -841,6 +841,22 @@ NTSTATUS _wbint_ChangeMachineAccount(struct pipes_struct *p,
 		return NT_STATUS_REQUEST_NOT_ACCEPTED;
 	}
 
+	if (r->in.dcname != NULL && r->in.dcname[0] != '\0') {
+		invalidate_cm_connection(domain);
+		TALLOC_FREE(domain->dcname);
+
+		domain->dcname = talloc_strdup(domain, r->in.dcname);
+		if (domain->dcname == NULL) {
+			status = NT_STATUS_NO_MEMORY;
+			goto done;
+		}
+		domain->force_dc = true;
+
+		DBG_NOTICE("attempt connection to change trust account "
+			   "password for %s at %s\n",
+			   domain->name, domain->dcname);
+	}
+
 	status = cm_connect_netlogon_secure(domain,
 					    &netlogon_pipe,
 					    &netlogon_creds_ctx);
@@ -863,9 +879,13 @@ NTSTATUS _wbint_ChangeMachineAccount(struct pipes_struct *p,
 		NT_STATUS_IS_OK(status) ? "changed" : "unchanged"));
 
  done:
-	DEBUG(NT_STATUS_IS_OK(status) ? 5 : 2,
-	      ("Changing the trust account password for domain %s returned %s\n",
-	       domain->name, nt_errstr(status)));
+	DEBUG(NT_STATUS_IS_OK(status) ? 5 :
+	      domain->force_dc ? 0 : 2,
+	      ("Changing the trust account password for domain %s at %s "
+	       "(forced: %s) returned %s\n",
+	       domain->name, domain->dcname, domain->force_dc ? "yes" : "no",
+	       nt_errstr(status)));
+	domain->force_dc = false;
 
 	return status;
 }