s3:libsmb: add trust_pw_change()
authorStefan Metzmacher <metze@samba.org>
Sun, 15 Sep 2013 11:19:52 +0000 (13:19 +0200)
committerStefan Metzmacher <metze@samba.org>
Tue, 7 Jan 2014 11:47:12 +0000 (12:47 +0100)
This protects the password change using a domain specific g_lock,
so multiple parts 'net rpc', 'rpcclient', 'winbindd', 'wbinfo --change-secret'
even on multiple cluster nodes doesn't race anymore.

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
source3/include/proto.h
source3/libsmb/trusts_util.c

index fe81145f6e70a595d6d021607b5c9c88327d7361..d2b6836bb2f8425c76f2d7684e9e2da11d07a01d 100644 (file)
@@ -977,6 +977,14 @@ void update_trustdom_cache( void );
 NTSTATUS trust_pw_find_change_and_store_it(struct rpc_pipe_client *cli, 
                                           TALLOC_CTX *mem_ctx, 
                                           const char *domain) ;
+struct netlogon_creds_cli_context;
+struct messaging_context;
+struct dcerpc_binding_handle;
+NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
+                        struct messaging_context *msg_ctx,
+                        struct dcerpc_binding_handle *b,
+                        const char *domain,
+                        bool force);
 
 /* The following definitions come from param/loadparm.c  */
 
index 52fb48125a2da9b5e2de212aac19572aee49ef9a..b1bc006df22d348128c220d2216bf0dd3e56cc23 100644 (file)
 
 #include "includes.h"
 #include "../libcli/auth/libcli_auth.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 "secrets.h"
 #include "passdb.h"
 #include "libsmb/libsmb.h"
+#include "source3/include/messages.h"
+#include "source3/include/g_lock.h"
 
 /*********************************************************
  Change the domain password on the PDC.
@@ -113,3 +116,179 @@ NTSTATUS trust_pw_find_change_and_store_it(struct rpc_pipe_client *cli,
 
        return nt_status;
 }
+
+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)
+{
+       g_lock_unlock(state->g_ctx, state->g_lock_key);
+       return 0;
+}
+
+NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context,
+                        struct messaging_context *msg_ctx,
+                        struct dcerpc_binding_handle *b,
+                        const char *domain,
+                        bool force)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct trust_pw_change_state *state;
+       struct samr_Password current_nt_hash;
+       const struct samr_Password *previous_nt_hash = NULL;
+       enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL;
+       const char *account_name;
+       char *new_trust_passwd;
+       char *pwd;
+       struct dom_sid sid;
+       time_t pass_last_set_time;
+       struct timeval g_timeout = { 0, };
+       int timeout = 0;
+       struct timeval tv = { 0, };
+       NTSTATUS status;
+
+       state = talloc_zero(frame, struct trust_pw_change_state);
+       if (state == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       state->g_ctx = g_lock_ctx_init(state, msg_ctx);
+       if (state->g_ctx == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       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;
+       }
+
+       g_timeout = timeval_current_ofs(10, 0);
+       status = g_lock_lock(state->g_ctx,
+                            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;
+       }
+
+       talloc_set_destructor(state, trust_pw_change_state_destructor);
+
+       if (!get_trust_pw_hash(domain, current_nt_hash.hash,
+                              &account_name,
+                              &sec_channel_type)) {
+               DEBUG(0, ("could not fetch domain secrets for domain %s!\n", domain));
+               TALLOC_FREE(frame);
+               return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+       }
+
+       switch (sec_channel_type) {
+       case SEC_CHAN_WKSTA:
+               pwd = secrets_fetch_machine_password(domain,
+                                                    &pass_last_set_time,
+                                                    NULL);
+               if (pwd == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+               }
+               break;
+       case SEC_CHAN_DOMAIN:
+               if (!pdb_get_trusteddom_pw(domain, &pwd, &sid, &pass_last_set_time)) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE;
+               }
+               break;
+       default:
+               TALLOC_FREE(frame);
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+
+       timeout = lp_machine_password_timeout();
+       if (timeout == 0) {
+               if (!force) {
+                       DEBUG(10,("machine password never expires\n"));
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_OK;
+               }
+       }
+
+       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;
+       }
+
+       /* Create a random machine account password */
+       new_trust_passwd = generate_random_password(frame,
+                               DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH,
+                               DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+       if (new_trust_passwd == NULL) {
+               DEBUG(0, ("generate_random_password failed\n"));
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       status = netlogon_creds_cli_auth(context, b,
+                                        current_nt_hash,
+                                        previous_nt_hash);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return status;
+       }
+
+       status = netlogon_creds_cli_ServerPasswordSet(context, b,
+                                                     new_trust_passwd, NULL);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return 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.
+        */
+
+       switch (sec_channel_type) {
+
+       case SEC_CHAN_WKSTA:
+               if (!secrets_store_machine_password(new_trust_passwd, domain, sec_channel_type)) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
+               break;
+
+       case SEC_CHAN_DOMAIN:
+               /*
+                * we need to get the sid first for the
+                * pdb_set_trusteddom_pw call
+                */
+               if (!pdb_set_trusteddom_pw(domain, new_trust_passwd, &sid)) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
+               break;
+
+       default:
+               break;
+       }
+
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
+}