From 2a3f264b310b38b855800d5103fb3afd2a23fcb4 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 22 May 2017 18:07:21 +0200 Subject: [PATCH] s3:trusts_util: try to rollback to the current password if the remote change failed If a server has the RefusePasswordChange=1 under HKLM\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters, it will reject NetrServerPasswordSet2() with NT_STATUS_WRONG_PASSWORD. This results in a successful local change, but a failing remote change, which means the domain membership is broken (as we don't fallback to the previous password for ntlmssp nor kerberos yet). Note this is only a temporary fix, it still has potential problems, as we also rollback, e.g. if the password was changed, but we didn't get the servers response. BUG: https://bugzilla.samba.org/show_bug.cgi?id=12782 Signed-off-by: Stefan Metzmacher --- source3/libsmb/trusts_util.c | 93 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/source3/libsmb/trusts_util.c b/source3/libsmb/trusts_util.c index 2cc6264..a3dad02 100644 --- a/source3/libsmb/trusts_util.c +++ b/source3/libsmb/trusts_util.c @@ -103,6 +103,62 @@ char *trust_pw_new_value(TALLOC_CTX *mem_ctx, return generate_random_machine_password(mem_ctx, min, max); } +static bool trust_pw_change_rollback(const char *domain, + const struct dom_sid *domain_sid, + enum netr_SchannelType sec_channel_type, + const char *current_trust_passwd, + const char *previous_trust_passwd) +{ + /* + * we use the current time in order to avoid + * repeating failures. + */ + time_t now = time(NULL); + bool ok; + + switch (sec_channel_type) { + + case SEC_CHAN_WKSTA: + case SEC_CHAN_BDC: + ok = secrets_store_machine_pw_sync(current_trust_passwd, + previous_trust_passwd, + domain, NULL /* realm */, + NULL /* salting_principal */, + 0 /* supported_enc_types */, + domain_sid, now, + sec_channel_type, + false); /* delete_join */ + if (!ok) { + DEBUG(0, ("secrets_store_machine_pw_sync() rollback " + "failed for domain %s!\n", + domain)); + return false; + } + break; + + case SEC_CHAN_DNS_DOMAIN: + case SEC_CHAN_DOMAIN: + /* + * we need to get the sid first for the + * pdb_set_trusteddom_pw call + */ + ok = pdb_set_trusteddom_pw(domain, current_trust_passwd, + domain_sid); + if (!ok) { + DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n", + domain)); + return false; + } + break; + + default: + smb_panic("Unsupported secure channel type"); + break; + } + + return true; +} + NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, struct messaging_context *msg_ctx, struct dcerpc_binding_handle *b, @@ -116,6 +172,7 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, const struct samr_Password *current_nt_hash = NULL; const struct samr_Password *previous_nt_hash = NULL; enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL; + struct dom_sid domain_sid = { 0, }; time_t pass_last_set_time; uint32_t old_version = 0; struct pdb_trusted_domain *td = NULL; @@ -123,6 +180,8 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, int timeout = 0; struct timeval tv = { 0, }; char *new_trust_passwd = NULL; + const char *current_trust_passwd = NULL; + const char *previous_trust_passwd = NULL; uint32_t new_version = 0; uint32_t *new_trust_version = NULL; NTSTATUS status; @@ -180,12 +239,21 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, old_version = cli_credentials_get_kvno(creds); pass_last_set_time = cli_credentials_get_password_last_changed_time(creds); sec_channel_type = cli_credentials_get_secure_channel_type(creds); + current_trust_passwd = cli_credentials_get_password(creds); + previous_trust_passwd = cli_credentials_get_old_password(creds); new_version = old_version + 1; switch (sec_channel_type) { case SEC_CHAN_WKSTA: case SEC_CHAN_BDC: + ok = secrets_fetch_domain_sid(domain, &domain_sid); + if (!ok) { + DEBUG(0, ("secrets_fetch_domain_sid() failed for domain %s!\n", + domain)); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_ERROR; + } break; case SEC_CHAN_DNS_DOMAIN: case SEC_CHAN_DOMAIN: @@ -197,6 +265,7 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, return status; } + domain_sid = td->security_identifier; new_trust_version = &new_version; break; default: @@ -321,6 +390,30 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, current_timestring(talloc_tos(), false), __func__, domain, context_name, nt_errstr(status))); + + /* + * For now all we can do is trying to undo + * the local change. + * + * We will have move advanced handling in future. + */ + DEBUG(0,("%s : %s(%s) Trying a local rollback\n", + current_timestring(talloc_tos(), false), + __func__, domain)); + ok = trust_pw_change_rollback(domain, &domain_sid, + sec_channel_type, + current_trust_passwd, + previous_trust_passwd); + if (!ok) { + DEBUG(0,("%s : %s(%s) trust_pw_change_rollback() failed.\n", + current_timestring(talloc_tos(), false), + __func__, domain)); + TALLOC_FREE(frame); + return status; + } + DEBUG(0,("%s : %s(%s) Local rollback done.\n", + current_timestring(talloc_tos(), false), + __func__, domain)); TALLOC_FREE(frame); return status; } -- 1.9.1