s3/libsmb: clang: value stored to 'idx_current' is never read
[samba.git] / source3 / libsmb / trusts_util.c
index dc2cf03a044fc877707f145edbb80bb7cf52ffb8..55e3c74494ab0b167ce8fc8f4d81a72e2ae20b1c 100644 (file)
 
 #include "includes.h"
 #include "../libcli/auth/libcli_auth.h"
-#include "../librpc/gen_ndr/ndr_lsa_c.h"
-#include "rpc_client/cli_lsarpc.h"
+#include "../libcli/auth/netlogon_creds_cli.h"
 #include "rpc_client/cli_netlogon.h"
 #include "rpc_client/cli_pipe.h"
 #include "../librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/secrets.h"
 #include "secrets.h"
 #include "passdb.h"
 #include "libsmb/libsmb.h"
+#include "source3/include/messages.h"
+#include "source3/include/g_lock.h"
+#include "lib/util/util_tdb.h"
 
 /*********************************************************
  Change the domain password on the PDC.
- Store the password ourselves, but use the supplied password
Caller must have already setup the connection to the NETLOGON pipe
+ Do most of the legwork ourselfs.  Caller must have
+ already setup the connection to the NETLOGON pipe
 **********************************************************/
 
-NTSTATUS trust_pw_change_and_store_it(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx, 
-                                     const char *domain,
-                                     const char *account_name,
-                                     unsigned char orig_trust_passwd_hash[16],
-                                     enum netr_SchannelType sec_channel_type)
+struct trust_pw_change_state {
+       struct g_lock_ctx *g_ctx;
+       char *g_lock_key;
+};
+
+static int trust_pw_change_state_destructor(struct trust_pw_change_state *state)
 {
-       unsigned char new_trust_passwd_hash[16];
-       char *new_trust_passwd;
-       NTSTATUS nt_status;
+       g_lock_unlock(state->g_ctx,
+                     string_term_tdb_data(state->g_lock_key));
+       return 0;
+}
+
+char *trust_pw_new_value(TALLOC_CTX *mem_ctx,
+                        enum netr_SchannelType sec_channel_type,
+                        int security)
+{
+       /*
+        * use secure defaults.
+        */
+       size_t min = 128;
+       size_t max = 255;
 
        switch (sec_channel_type) {
        case SEC_CHAN_WKSTA:
+       case SEC_CHAN_BDC:
+               if (security == SEC_DOMAIN) {
+                       /*
+                        * The maximum length of a trust account password.
+                        * Used when we randomly create it, 15 char passwords
+                        * exceed NT4's max password length.
+                        */
+                       min = 14;
+                       max = 14;
+               }
+               break;
+       case SEC_CHAN_DNS_DOMAIN:
+               /*
+                * new_len * 2 = 498 bytes is the largest possible length
+                * NL_PASSWORD_VERSION consumes the rest of the possible 512 bytes
+                * and a confounder with at least 2 bytes is required.
+                *
+                * Windows uses new_len = 120 => 240 bytes (utf16)
+                */
+               min = 120;
+               max = 120;
+               break;
        case SEC_CHAN_DOMAIN:
+               /*
+                * The maximum length of a trust account password.
+                * Used when we randomly create it, 15 char passwords
+                * exceed NT4's max password length.
+                */
+               min = 14;
+               max = 14;
                break;
        default:
-               return NT_STATUS_NOT_SUPPORTED;
-       }
-
-       /* Create a random machine account password */
-       new_trust_passwd = generate_random_str(mem_ctx, DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
-
-       if (new_trust_passwd == NULL) {
-               DEBUG(0, ("talloc_strdup failed\n"));
-               return NT_STATUS_NO_MEMORY;
+               break;
        }
 
-       E_md4hash(new_trust_passwd, new_trust_passwd_hash);
+       /*
+        * Create a random machine account password
+        * We create a random buffer and convert that to utf8.
+        * This is similar to what windows is doing.
+        */
+       return generate_random_machine_password(mem_ctx, min, max);
+}
 
-       nt_status = rpccli_netlogon_set_trust_password(cli, mem_ctx,
-                                                      account_name,
-                                                      orig_trust_passwd_hash,
-                                                      new_trust_passwd,
-                                                      new_trust_passwd_hash,
-                                                      sec_channel_type);
+/*
+ * Temporary function to wrap cli_auth in a lck
+ */
 
-       if (NT_STATUS_IS_OK(nt_status)) {
-               DEBUG(3,("%s : trust_pw_change_and_store_it: Changed password.\n", 
-                        current_timestring(talloc_tos(), False)));
-               /*
-                * Return the result of trying to write the new password
-                * back into the trust account file.
-                */
+static NTSTATUS netlogon_creds_cli_lck_auth(
+       struct netlogon_creds_cli_context *context,
+       struct dcerpc_binding_handle *b,
+       uint8_t num_nt_hashes,
+       const struct samr_Password * const *nt_hashes,
+       uint8_t *idx_nt_hashes)
+{
+       struct netlogon_creds_cli_lck *lck;
+       NTSTATUS status;
 
-               switch (sec_channel_type) {
-
-               case SEC_CHAN_WKSTA:
-                       if (!secrets_store_machine_password(new_trust_passwd, domain, sec_channel_type)) {
-                               nt_status = NT_STATUS_UNSUCCESSFUL;
-                       }
-                       break;
-
-               case SEC_CHAN_DOMAIN: {
-                       char *pwd;
-                       struct dom_sid sid;
-                       time_t pass_last_set_time;
-
-                       /* we need to get the sid first for the
-                        * pdb_set_trusteddom_pw call */
-
-                       if (!pdb_get_trusteddom_pw(domain, &pwd, &sid, &pass_last_set_time)) {
-                               nt_status = NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
-                       }
-                       if (!pdb_set_trusteddom_pw(domain, new_trust_passwd, &sid)) {
-                               nt_status = NT_STATUS_INTERNAL_DB_CORRUPTION;
-                       }
-                       break;
-               }
-               default:
-                       break;
-               }
+       status = netlogon_creds_cli_lck(
+               context, NETLOGON_CREDS_CLI_LCK_EXCLUSIVE,
+               talloc_tos(), &lck);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_WARNING("netlogon_creds_cli_lck failed: %s\n",
+                           nt_errstr(status));
+               return status;
        }
 
-       return nt_status;
-}
+       status = netlogon_creds_cli_auth(context, b, num_nt_hashes, nt_hashes,
+                                        idx_nt_hashes);
+       TALLOC_FREE(lck);
 
-/*********************************************************
- Change the domain password on the PDC.
- Do most of the legwork ourselfs.  Caller must have
- already setup the connection to the NETLOGON pipe
-**********************************************************/
+       return status;
+}
 
-NTSTATUS trust_pw_find_change_and_store_it(struct rpc_pipe_client *cli, 
-                                          TALLOC_CTX *mem_ctx, 
-                                          const char *domain) 
+NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
+                        struct messaging_context *msg_ctx,
+                        struct dcerpc_binding_handle *b,
+                        const char *domain,
+                        const char *dcname,
+                        bool force)
 {
-       unsigned char old_trust_passwd_hash[16];
+       TALLOC_CTX *frame = talloc_stackframe();
+       const char *context_name = NULL;
+       struct trust_pw_change_state *state;
+       struct cli_credentials *creds = NULL;
+       struct secrets_domain_info1 *info = NULL;
+       struct secrets_domain_info1_change *prev = NULL;
+       const struct samr_Password *current_nt_hash = NULL;
+       const struct samr_Password *previous_nt_hash = NULL;
+       uint8_t num_nt_hashes = 0;
+       uint8_t idx = 0;
+       const struct samr_Password *nt_hashes[1+3] = { NULL, };
+       uint8_t idx_nt_hashes = 0;
+       uint8_t idx_current = UINT8_MAX;
        enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL;
-       const char *account_name;
-
-       if (!get_trust_pw_hash(domain, old_trust_passwd_hash, &account_name,
-                              &sec_channel_type)) {
-               DEBUG(0, ("could not fetch domain secrets for domain %s!\n", domain));
-               return NT_STATUS_UNSUCCESSFUL;
+       time_t pass_last_set_time;
+       uint32_t old_version = 0;
+       struct pdb_trusted_domain *td = NULL;
+       struct timeval g_timeout = { 0, };
+       int timeout = 0;
+       struct timeval tv = { 0, };
+       char *new_trust_pw_str = NULL;
+       size_t len = 0;
+       DATA_BLOB new_trust_pw_blob = data_blob_null;
+       uint32_t new_version = 0;
+       uint32_t *new_trust_version = NULL;
+       NTSTATUS status;
+       bool ok;
+
+       state = talloc_zero(frame, struct trust_pw_change_state);
+       if (state == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
        }
 
-       return trust_pw_change_and_store_it(cli, mem_ctx, domain,
-                                           account_name,
-                                           old_trust_passwd_hash,
-                                           sec_channel_type);
-}
-
-/*********************************************************************
- Enumerate the list of trusted domains from a DC
-*********************************************************************/
-
-bool enumerate_domain_trusts( TALLOC_CTX *mem_ctx, const char *domain,
-                                     char ***domain_names, uint32 *num_domains,
-                                    struct dom_sid **sids )
-{
-       struct policy_handle    pol;
-       NTSTATUS status, result;
-       fstring         dc_name;
-       struct sockaddr_storage dc_ss;
-       uint32          enum_ctx = 0;
-       struct cli_state *cli = NULL;
-       struct rpc_pipe_client *lsa_pipe = NULL;
-       struct lsa_DomainList dom_list;
-       int i;
-       struct dcerpc_binding_handle *b = NULL;
-
-       *domain_names = NULL;
-       *num_domains = 0;
-       *sids = NULL;
-
-       /* lookup a DC first */
-
-       if ( !get_dc_name(domain, NULL, dc_name, &dc_ss) ) {
-               DEBUG(3,("enumerate_domain_trusts: can't locate a DC for domain %s\n",
-                       domain));
-               return False;
+       state->g_ctx = g_lock_ctx_init(state, msg_ctx);
+       if (state->g_ctx == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
        }
 
-       /* setup the anonymous connection */
+       state->g_lock_key = talloc_asprintf(state,
+                               "trust_password_change_%s",
+                               domain);
+       if (state->g_lock_key == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       status = cli_full_connection( &cli, lp_netbios_name(), dc_name, &dc_ss, 0, "IPC$", "IPC",
-               "", "", "", 0, Undefined);
-       if ( !NT_STATUS_IS_OK(status) )
-               goto done;
+       g_timeout = timeval_current_ofs(10, 0);
+       status = g_lock_lock(state->g_ctx,
+                            string_term_tdb_data(state->g_lock_key),
+                            G_LOCK_WRITE, g_timeout);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("could not get g_lock on [%s]!\n",
+                         state->g_lock_key));
+               TALLOC_FREE(frame);
+               return status;
+       }
 
-       /* open the LSARPC_PIPE */
+       talloc_set_destructor(state, trust_pw_change_state_destructor);
 
-       status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc.syntax_id,
-                                         &lsa_pipe);
+       status = pdb_get_trust_credentials(domain, NULL, frame, &creds);
        if (!NT_STATUS_IS_OK(status)) {
-               goto done;
+               DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n",
+                         domain, nt_errstr(status)));
+               TALLOC_FREE(frame);
+               return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
        }
 
-       b = lsa_pipe->binding_handle;
+       current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
+       if (current_nt_hash == NULL) {
+               DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
+                         domain));
+               TALLOC_FREE(frame);
+               return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+       }
+       previous_nt_hash = cli_credentials_get_old_nt_hash(creds, frame);
 
-       /* get a handle */
+       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);
 
-       status = rpccli_lsa_open_policy(lsa_pipe, mem_ctx, True,
-               LSA_POLICY_VIEW_LOCAL_INFORMATION, &pol);
-       if ( !NT_STATUS_IS_OK(status) )
-               goto done;
+       new_version = old_version + 1;
 
-       /* Lookup list of trusted domains */
+       switch (sec_channel_type) {
+       case SEC_CHAN_WKSTA:
+       case SEC_CHAN_BDC:
+               break;
+       case SEC_CHAN_DNS_DOMAIN:
+       case SEC_CHAN_DOMAIN:
+               status = pdb_get_trusted_domain(frame, domain, &td);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n",
+                                 domain, nt_errstr(status)));
+                       TALLOC_FREE(frame);
+                       return status;
+               }
 
-       status = dcerpc_lsa_EnumTrustDom(b, mem_ctx,
-                                        &pol,
-                                        &enum_ctx,
-                                        &dom_list,
-                                        (uint32_t)-1,
-                                        &result);
-       if ( !NT_STATUS_IS_OK(status) )
-               goto done;
-       if (!NT_STATUS_IS_OK(result)) {
-               status = result;
-               goto done;
+               new_trust_version = &new_version;
+               break;
+       default:
+               TALLOC_FREE(frame);
+               return NT_STATUS_NOT_SUPPORTED;
        }
 
-       *num_domains = dom_list.count;
+       timeout = lp_machine_password_timeout();
+       if (timeout == 0) {
+               if (!force) {
+                       DEBUG(10,("machine password never expires\n"));
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_OK;
+               }
+       }
 
-       *domain_names = talloc_zero_array(mem_ctx, char *, *num_domains);
-       if (!*domain_names) {
-               status = NT_STATUS_NO_MEMORY;
-               goto done;
+       tv.tv_sec = pass_last_set_time;
+       DEBUG(10, ("password last changed %s\n",
+                  timeval_string(talloc_tos(), &tv, false)));
+       tv.tv_sec += timeout;
+       DEBUGADD(10, ("password valid until %s\n",
+                     timeval_string(talloc_tos(), &tv, false)));
+
+       if (!force && !timeval_expired(&tv)) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_OK;
        }
 
-       *sids = talloc_zero_array(mem_ctx, struct dom_sid, *num_domains);
-       if (!*sids) {
-               status = NT_STATUS_NO_MEMORY;
-               goto done;
+       context_name = netlogon_creds_cli_debug_string(context, talloc_tos());
+       if (context_name == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
        }
 
-       for (i=0; i< *num_domains; i++) {
-               (*domain_names)[i] = discard_const_p(char, dom_list.domains[i].name.string);
-               (*sids)[i] = *dom_list.domains[i].sid;
+       /*
+        * Create a random machine account password
+        * We create a random buffer and convert that to utf8.
+        * This is similar to what windows is doing.
+        */
+       new_trust_pw_str = trust_pw_new_value(frame, sec_channel_type,
+                                             lp_security());
+       if (new_trust_pw_str == NULL) {
+               DEBUG(0, ("trust_pw_new_value() failed\n"));
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
        }
 
-done:
-       /* cleanup */
-       if (cli) {
-               DEBUG(10,("enumerate_domain_trusts: shutting down connection...\n"));
-               cli_shutdown( cli );
+       len = strlen(new_trust_pw_str);
+       ok = convert_string_talloc(frame, CH_UNIX, CH_UTF16,
+                                  new_trust_pw_str, len,
+                                  (void **)&new_trust_pw_blob.data,
+                                  &new_trust_pw_blob.length);
+       if (!ok) {
+               status = NT_STATUS_UNMAPPABLE_CHARACTER;
+               if (errno == ENOMEM) {
+                       status = NT_STATUS_NO_MEMORY;
+               }
+               DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) "
+                       "failed for of %s - %s\n",
+                       domain, nt_errstr(status));
+               TALLOC_FREE(frame);
+               return status;
        }
 
-       return NT_STATUS_IS_OK(status);
-}
+       switch (sec_channel_type) {
 
-NTSTATUS change_trust_account_password( const char *domain, const char *remote_machine)
-{
-       NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
-       struct sockaddr_storage pdc_ss;
-       fstring dc_name;
-       struct cli_state *cli = NULL;
-       struct rpc_pipe_client *netlogon_pipe = NULL;
+       case SEC_CHAN_WKSTA:
+       case SEC_CHAN_BDC:
+               status = secrets_prepare_password_change(domain, dcname,
+                                                        new_trust_pw_str,
+                                                        frame, &info, &prev);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
+                                 domain));
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
+               TALLOC_FREE(new_trust_pw_str);
+
+               if (prev != NULL) {
+                       /*
+                        * We had a failure before we changed the password.
+                        */
+                       nt_hashes[idx++] = &prev->password->nt_hash;
+
+                       DEBUG(0,("%s : %s(%s): A password change was already "
+                                "started against '%s' at %s. Trying to "
+                                "recover...\n",
+                                current_timestring(talloc_tos(), false),
+                                __func__, domain,
+                                prev->password->change_server,
+                                nt_time_string(talloc_tos(),
+                                prev->password->change_time)));
+                       DEBUG(0,("%s : %s(%s): Last failure local[%s] remote[%s] "
+                                "against '%s' at %s.\n",
+                                current_timestring(talloc_tos(), false),
+                                __func__, domain,
+                                nt_errstr(prev->local_status),
+                                nt_errstr(prev->remote_status),
+                                prev->change_server,
+                                nt_time_string(talloc_tos(),
+                                prev->change_time)));
+               }
+
+               idx_current = idx;
+               nt_hashes[idx++] = &info->password->nt_hash;
+               if (info->old_password != NULL) {
+                       nt_hashes[idx++] = &info->old_password->nt_hash;
+               }
+               if (info->older_password != NULL) {
+                       nt_hashes[idx++] = &info->older_password->nt_hash;
+               }
+
+               /*
+                * We use the password that's already persistent in
+                * our database in order to handle failures.
+                */
+               data_blob_clear_free(&new_trust_pw_blob);
+               new_trust_pw_blob = info->next_change->password->cleartext_blob;
+               break;
 
-       DEBUG(5,("change_trust_account_password: Attempting to change trust account password in domain %s....\n",
-               domain));
+       case SEC_CHAN_DNS_DOMAIN:
+       case SEC_CHAN_DOMAIN:
+               idx_current = idx;
+               nt_hashes[idx++] = current_nt_hash;
+               if (previous_nt_hash != NULL) {
+                       nt_hashes[idx++] = previous_nt_hash;
+               }
+               break;
 
-       if (remote_machine == NULL || !strcmp(remote_machine, "*")) {
-               /* Use the PDC *only* for this */
+       default:
+               smb_panic("Unsupported secure channel type");
+               break;
+       }
+       num_nt_hashes = idx;
+
+       DEBUG(0,("%s : %s(%s): Verifying passwords remotely %s.\n",
+                current_timestring(talloc_tos(), false),
+                __func__, domain, context_name));
+
+       /*
+        * Check which password the dc knows about.
+        *
+        * TODO:
+        * If the previous password is the only password in common with the dc,
+        * we better skip the password change, or use something like
+        * ServerTrustPasswordsGet() or netr_ServerGetTrustInfo() to fix our
+        * local secrets before doing the change.
+        */
+       status = netlogon_creds_cli_lck_auth(context, b,
+                                            num_nt_hashes,
+                                            nt_hashes,
+                                            &idx_nt_hashes);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old passwords (%u) - %s!\n",
+                         context_name, num_nt_hashes, nt_errstr(status)));
+               TALLOC_FREE(frame);
+               return status;
+       }
 
-               if ( !get_pdc_ip(domain, &pdc_ss) ) {
-                       DEBUG(0,("Can't get IP for PDC for domain %s\n", domain));
-                       goto failed;
+       if (prev != NULL && idx_nt_hashes == 0) {
+               DEBUG(0,("%s : %s(%s): Verified new password remotely "
+                        "without changing %s\n",
+                        current_timestring(talloc_tos(), false),
+                        __func__, domain, context_name));
+
+               status = secrets_finish_password_change(prev->password->change_server,
+                                                       prev->password->change_time,
+                                                       info);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n",
+                                 domain));
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
                }
 
-               if ( !name_status_find( domain, 0x1b, 0x20, &pdc_ss, dc_name) )
-                       goto failed;
-       } else {
-               /* supoport old deprecated "smbpasswd -j DOMAIN -r MACHINE" behavior */
-               fstrcpy( dc_name, remote_machine );
+               DEBUG(0,("%s : %s(%s): Recovered previous password change.\n",
+                        current_timestring(talloc_tos(), false),
+                        __func__, domain));
+               TALLOC_FREE(frame);
+               return NT_STATUS_OK;
        }
 
-       /* if this next call fails, then give up.  We can't do
-          password changes on BDC's  --jerry */
-
-       if (!NT_STATUS_IS_OK(cli_full_connection(&cli, lp_netbios_name(), dc_name,
-                                          NULL, 0,
-                                          "IPC$", "IPC",
-                                          "", "",
-                                          "", 0, Undefined))) {
-               DEBUG(0,("modify_trust_password: Connection to %s failed!\n", dc_name));
-               nt_status = NT_STATUS_UNSUCCESSFUL;
-               goto failed;
+       if (idx_nt_hashes != idx_current) {
+               DEBUG(0,("%s : %s(%s): Verified older password remotely "
+                        "skip changing %s\n",
+                        current_timestring(talloc_tos(), false),
+                        __func__, domain, context_name));
+
+               if (info == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+               }
+
+               status = secrets_defer_password_change(dcname,
+                                       NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE,
+                                       NT_STATUS_NOT_COMMITTED,
+                                       info);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0, ("secrets_defer_password_change() failed for domain %s!\n",
+                                 domain));
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
+               TALLOC_FREE(frame);
+               return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
        }
 
+       DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n",
+                current_timestring(talloc_tos(), false),
+                __func__, domain, context_name));
+
        /*
-        * Ok - we have an anonymous connection to the IPC$ share.
-        * Now start the NT Domain stuff :-).
+        * Return the result of trying to write the new password
+        * back into the trust account file.
         */
 
-       /* Shouldn't we open this with schannel ? JRA. */
+       switch (sec_channel_type) {
+
+       case SEC_CHAN_WKSTA:
+       case SEC_CHAN_BDC:
+               /*
+                * we called secrets_prepare_password_change() above.
+                */
+               break;
 
-       nt_status = cli_rpc_pipe_open_noauth(
-               cli, &ndr_table_netlogon.syntax_id, &netlogon_pipe);
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               DEBUG(0,("modify_trust_password: unable to open the domain client session to machine %s. Error was : %s.\n",
-                       dc_name, nt_errstr(nt_status)));
-               cli_shutdown(cli);
-               cli = NULL;
-               goto failed;
+       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, new_trust_pw_str,
+                                          &td->security_identifier);
+               if (!ok) {
+                       DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n",
+                                 domain));
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
+               TALLOC_FREE(new_trust_pw_str);
+               break;
+
+       default:
+               smb_panic("Unsupported secure channel type");
+               break;
        }
 
-       nt_status = trust_pw_find_change_and_store_it(
-               netlogon_pipe, netlogon_pipe, domain);
+       DEBUG(0,("%s : %s(%s): Changed password locally\n",
+                current_timestring(talloc_tos(), false), __func__, domain));
 
-       cli_shutdown(cli);
-       cli = NULL;
+       status = netlogon_creds_cli_ServerPasswordSet(context, b,
+                                                     &new_trust_pw_blob,
+                                                     new_trust_version);
+       if (!NT_STATUS_IS_OK(status)) {
+               NTSTATUS status2;
+               const char *fn = NULL;
+
+               ok = dcerpc_binding_handle_is_connected(b);
+
+               DEBUG(0,("%s : %s(%s) remote password change with %s failed "
+                        "- %s (%s)\n",
+                        current_timestring(talloc_tos(), false),
+                        __func__, domain, context_name,
+                        nt_errstr(status),
+                        ok ? "connected": "disconnected"));
+
+               if (!ok) {
+                       /*
+                        * The connection is broken, we don't
+                        * know if the password was changed,
+                        * we hope to have more luck next time.
+                        */
+                       status2 = secrets_failed_password_change(dcname,
+                                                       NT_STATUS_NOT_COMMITTED,
+                                                       status,
+                                                       info);
+                       fn = "secrets_failed_password_change";
+               } else {
+                       /*
+                        * The server rejected the change, we don't
+                        * retry and defer the change to the next
+                        * "machine password timeout" interval.
+                        */
+                       status2 = secrets_defer_password_change(dcname,
+                                                       NT_STATUS_NOT_COMMITTED,
+                                                       status,
+                                                       info);
+                       fn = "secrets_defer_password_change";
+               }
+               if (!NT_STATUS_IS_OK(status2)) {
+                       DEBUG(0, ("%s() failed for domain %s!\n",
+                                 fn, domain));
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
 
-failed:
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               DEBUG(0,("%s : change_trust_account_password: Failed to change password for domain %s.\n",
-                       current_timestring(talloc_tos(), False), domain));
+               TALLOC_FREE(frame);
+               return status;
        }
-       else
-               DEBUG(5,("change_trust_account_password: sucess!\n"));
 
-       return nt_status;
+       DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n",
+                current_timestring(talloc_tos(), false),
+                __func__, domain, context_name));
+
+       switch (sec_channel_type) {
+
+       case SEC_CHAN_WKSTA:
+       case SEC_CHAN_BDC:
+               status = secrets_finish_password_change(
+                                       info->next_change->change_server,
+                                       info->next_change->change_time,
+                                       info);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0, ("secrets_finish_password_change() failed for domain %s!\n",
+                                 domain));
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
+
+               DEBUG(0,("%s : %s(%s): Finished password change.\n",
+                        current_timestring(talloc_tos(), false),
+                        __func__, domain));
+               break;
+
+       case SEC_CHAN_DNS_DOMAIN:
+       case SEC_CHAN_DOMAIN:
+               /*
+                * we used pdb_set_trusteddom_pw().
+                */
+               break;
+
+       default:
+               smb_panic("Unsupported secure channel type");
+               break;
+       }
+
+       ok = cli_credentials_set_utf16_password(creds,
+                                               &new_trust_pw_blob,
+                                               CRED_SPECIFIED);
+       if (!ok) {
+               DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
+                         domain));
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       current_nt_hash = cli_credentials_get_nt_hash(creds, frame);
+       if (current_nt_hash == NULL) {
+               DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n",
+                         domain));
+               TALLOC_FREE(frame);
+               return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+       }
+
+       /*
+        * Now we verify the new password.
+        */
+       idx = 0;
+       nt_hashes[idx++] = current_nt_hash;
+       num_nt_hashes = idx;
+       status = netlogon_creds_cli_lck_auth(context, b,
+                                            num_nt_hashes,
+                                            nt_hashes,
+                                            &idx_nt_hashes);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n",
+                         context_name, nt_errstr(status)));
+               TALLOC_FREE(frame);
+               return status;
+       }
+
+       DEBUG(0,("%s : %s(%s): Verified new password remotely using %s\n",
+                current_timestring(talloc_tos(), false),
+                __func__, domain, context_name));
+
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
 }