*/
#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.
+ Do most of the legwork ourselfs. Caller must have
+ already setup the connection to the NETLOGON pipe
+**********************************************************/
- Just changes the password betwen the two values specified.
+struct trust_pw_change_state {
+ struct g_lock_ctx *g_ctx;
+ char *g_lock_key;
+};
- Caller must have the cli connected to the netlogon pipe
- already.
-**********************************************************/
+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;
+}
-static NTSTATUS just_change_the_password(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx,
- const unsigned char orig_trust_passwd_hash[16],
- const unsigned char new_trust_passwd_hash[16],
- uint32 sec_channel_type)
+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)
{
- NTSTATUS result;
-
- /* Check if the netlogon pipe is open using schannel. If so we
- already have valid creds. If not we must set them up. */
-
- if (cli->auth.auth_type != PIPE_AUTH_TYPE_SCHANNEL) {
- uint32 neg_flags = NETLOGON_NEG_SELECT_AUTH2_FLAGS;
-
- result = rpccli_netlogon_setup_creds(cli,
- cli->cli->desthost, /* server name */
- lp_workgroup(), /* domain */
- global_myname(), /* client name */
- global_myname(), /* machine account name */
- orig_trust_passwd_hash,
- sec_channel_type,
- &neg_flags);
-
- if (!NT_STATUS_IS_OK(result)) {
- DEBUG(3,("just_change_the_password: unable to setup creds (%s)!\n",
- nt_errstr(result)));
- return result;
- }
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char *context_name = NULL;
+ struct trust_pw_change_state *state;
+ struct cli_credentials *creds = NULL;
+ 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;
+ 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, };
+ size_t new_len = DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH;
+ uint8_t new_password_buffer[256 * 2] = { 0, };
+ char *new_trust_passwd = NULL;
+ size_t len = 0;
+ 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;
}
- result = rpccli_net_srv_pwset(cli, mem_ctx, global_myname(), new_trust_passwd_hash);
+ state->g_ctx = g_lock_ctx_init(state, msg_ctx);
+ if (state->g_ctx == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
- if (!NT_STATUS_IS_OK(result)) {
- DEBUG(0,("just_change_the_password: unable to change password (%s)!\n",
- nt_errstr(result)));
+ 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;
}
- return result;
-}
-/*********************************************************
- 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
-**********************************************************/
+ 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;
+ }
-NTSTATUS trust_pw_change_and_store_it(struct rpc_pipe_client *cli, TALLOC_CTX *mem_ctx,
- const char *domain,
- unsigned char orig_trust_passwd_hash[16],
- uint32 sec_channel_type)
-{
- unsigned char new_trust_passwd_hash[16];
- char *new_trust_passwd;
- char *str;
- NTSTATUS nt_status;
-
- /* Create a random machine account password */
- str = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
-
- if ((new_trust_passwd = talloc_strdup(mem_ctx, str)) == NULL) {
- DEBUG(0, ("talloc_strdup failed\n"));
- return NT_STATUS_NO_MEMORY;
+ talloc_set_destructor(state, trust_pw_change_state_destructor);
+
+ status = pdb_get_trust_credentials(domain, NULL, frame, &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ 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;
}
-
- E_md4hash(new_trust_passwd, new_trust_passwd_hash);
- nt_status = just_change_the_password(cli, mem_ctx, orig_trust_passwd_hash,
- new_trust_passwd_hash, sec_channel_type);
-
- if (NT_STATUS_IS_OK(nt_status)) {
- DEBUG(3,("%s : trust_pw_change_and_store_it: Changed password.\n",
- current_timestring(False)));
- /*
- * Return the result of trying to write the new password
- * back into the trust account file.
- */
- if (!secrets_store_machine_password(new_trust_passwd, domain, sec_channel_type)) {
- nt_status = NT_STATUS_UNSUCCESSFUL;
- }
+ 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;
}
- return nt_status;
-}
+ 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);
-/*********************************************************
- Change the domain password on the PDC.
- Do most of the legwork ourselfs. Caller must have
- already setup the connection to the NETLOGON pipe
-**********************************************************/
+ new_version = old_version + 1;
-NTSTATUS trust_pw_find_change_and_store_it(struct rpc_pipe_client *cli,
- TALLOC_CTX *mem_ctx,
- const char *domain)
-{
- unsigned char old_trust_passwd_hash[16];
- uint32 sec_channel_type = 0;
-
- if (!secrets_fetch_trust_account_password(domain,
- old_trust_passwd_hash,
- NULL, &sec_channel_type)) {
- DEBUG(0, ("could not fetch domain secrets for domain %s!\n", domain));
- return NT_STATUS_UNSUCCESSFUL;
- }
-
- return trust_pw_change_and_store_it(cli, mem_ctx, domain,
- old_trust_passwd_hash,
- sec_channel_type);
-}
+ switch (sec_channel_type) {
+ case SEC_CHAN_WKSTA:
+ case SEC_CHAN_BDC:
+ 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.
+ */
+ new_len = 120;
+
+ /* fall through */
+ 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;
+ }
-/*********************************************************************
- Enumerate the list of trusted domains from a DC
-*********************************************************************/
+ new_trust_version = &new_version;
+ break;
+ default:
+ TALLOC_FREE(frame);
+ return NT_STATUS_NOT_SUPPORTED;
+ }
-BOOL enumerate_domain_trusts( TALLOC_CTX *mem_ctx, const char *domain,
- char ***domain_names, uint32 *num_domains,
- DOM_SID **sids )
-{
- POLICY_HND pol;
- NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
- fstring dc_name;
- struct in_addr dc_ip;
- uint32 enum_ctx = 0;
- struct cli_state *cli = NULL;
- struct rpc_pipe_client *lsa_pipe;
- BOOL retry;
+ 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)));
- *domain_names = NULL;
- *num_domains = 0;
- *sids = NULL;
+ if (!force && !timeval_expired(&tv)) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
- /* lookup a DC first */
+ context_name = netlogon_creds_cli_debug_string(context, talloc_tos());
+ if (context_name == NULL) {
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
- if ( !get_dc_name(domain, NULL, dc_name, &dc_ip) ) {
- DEBUG(3,("enumerate_domain_trusts: can't locate a DC for domain %s\n",
- domain));
- return False;
+ /*
+ * Create a random machine account password
+ * We create a random buffer and convert that to utf8.
+ * This is similar to what windows is doing.
+ */
+ generate_secret_buffer(new_password_buffer, new_len * 2);
+ ok = convert_string_talloc(frame,
+ CH_UTF16MUNGED, CH_UTF8,
+ new_password_buffer, new_len * 2,
+ (void *)&new_trust_passwd, &len);
+ ZERO_STRUCT(new_password_buffer);
+ if (!ok) {
+ DEBUG(0, ("convert_string_talloc failed\n"));
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
}
- /* setup the anonymous connection */
+ /*
+ * We could use cli_credentials_get_old_nt_hash(creds, frame) to
+ * set previous_nt_hash.
+ *
+ * But we want to check if the dc has our current password and only do
+ * a change if that's the case. So we keep previous_nt_hash = NULL.
+ *
+ * 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_auth(context, b,
+ *current_nt_hash,
+ previous_nt_hash);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old password - %s!\n",
+ context_name, nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return status;
+ }
- result = cli_full_connection( &cli, global_myname(), dc_name, &dc_ip, 0, "IPC$", "IPC",
- "", "", "", 0, Undefined, &retry);
- if ( !NT_STATUS_IS_OK(result) )
- goto done;
+ DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n",
+ current_timestring(talloc_tos(), false),
+ __func__, domain, context_name));
+
+ /*
+ * Return the result of trying to write the new password
+ * back into the trust account file.
+ */
+
+ switch (sec_channel_type) {
+
+ case SEC_CHAN_WKSTA:
+ case SEC_CHAN_BDC:
+ ok = secrets_store_machine_password(new_trust_passwd, domain, sec_channel_type);
+ if (!ok) {
+ DEBUG(0, ("secrets_store_machine_password failed for domain %s!\n",
+ domain));
+ TALLOC_FREE(frame);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ break;
- /* open the LSARPC_PIPE */
+ 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_passwd,
+ &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;
+ }
+ break;
- lsa_pipe = cli_rpc_pipe_open_noauth( cli, PI_LSARPC, &result );
- if ( !lsa_pipe) {
- goto done;
+ default:
+ smb_panic("Unsupported secure channel type");
+ break;
}
- /* get a handle */
+ DEBUG(0,("%s : %s(%s): Changed password locally\n",
+ current_timestring(talloc_tos(), false), __func__, domain));
+
+ status = netlogon_creds_cli_ServerPasswordSet(context, b,
+ new_trust_passwd,
+ new_trust_version);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s : %s(%s) remote password change set with %s failed - %s\n",
+ current_timestring(talloc_tos(), false),
+ __func__, domain, context_name,
+ nt_errstr(status)));
+ TALLOC_FREE(frame);
+ return status;
+ }
- result = rpccli_lsa_open_policy(lsa_pipe, mem_ctx, True,
- POLICY_VIEW_LOCAL_INFORMATION, &pol);
- if ( !NT_STATUS_IS_OK(result) )
- goto done;
+ DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n",
+ current_timestring(talloc_tos(), false),
+ __func__, domain, context_name));
- /* Lookup list of trusted domains */
+ ok = cli_credentials_set_password(creds, new_trust_passwd, CRED_SPECIFIED);
+ if (!ok) {
+ DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n",
+ domain));
+ TALLOC_FREE(frame);
+ return NT_STATUS_NO_MEMORY;
+ }
- result = rpccli_lsa_enum_trust_dom(lsa_pipe, mem_ctx, &pol, &enum_ctx,
- num_domains, domain_names, sids);
- if ( !NT_STATUS_IS_OK(result) )
- goto done;
+ 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;
+ }
-done:
- /* cleanup */
- if (cli) {
- DEBUG(10,("enumerate_domain_trusts: shutting down connection...\n"));
- cli_shutdown( cli );
+ /*
+ * Now we verify the new password.
+ */
+ status = netlogon_creds_cli_auth(context, b,
+ *current_nt_hash,
+ NULL); /* previous_nt_hash */
+ 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;
}
- return NT_STATUS_IS_OK(result);
+ 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;
}