コード例 #1
0
ファイル: ntlmssp_client.c プロジェクト: DanilKorotenko/samba
NTSTATUS ntlmssp_client_challenge(struct gensec_security *gensec_security,
				  TALLOC_CTX *out_mem_ctx,
				  const DATA_BLOB in, DATA_BLOB *out)
{
	struct gensec_ntlmssp_context *gensec_ntlmssp =
		talloc_get_type_abort(gensec_security->private_data,
				      struct gensec_ntlmssp_context);
	struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp->ntlmssp_state;
	uint32_t chal_flags, ntlmssp_command, unkn1 = 0, unkn2 = 0;
	DATA_BLOB server_domain_blob;
	DATA_BLOB challenge_blob;
	DATA_BLOB target_info = data_blob(NULL, 0);
	char *server_domain;
	const char *chal_parse_string;
	const char *chal_parse_string_short = NULL;
	const char *auth_gen_string;
	DATA_BLOB lm_response = data_blob(NULL, 0);
	DATA_BLOB nt_response = data_blob(NULL, 0);
	DATA_BLOB session_key = data_blob(NULL, 0);
	DATA_BLOB lm_session_key = data_blob(NULL, 0);
	DATA_BLOB encrypted_session_key = data_blob(NULL, 0);
	NTSTATUS nt_status;
	int flags = 0;
	const char *user = NULL, *domain = NULL, *workstation = NULL;
	bool is_anonymous = false;
	const DATA_BLOB version_blob = ntlmssp_version_blob();

	TALLOC_CTX *mem_ctx = talloc_new(out_mem_ctx);
	if (!mem_ctx) {
		return NT_STATUS_NO_MEMORY;
	}

	if (!msrpc_parse(mem_ctx,
			 &in, "CdBd",
			 "NTLMSSP",
			 &ntlmssp_command,
			 &server_domain_blob,
			 &chal_flags)) {
		DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n"));
		dump_data(2, in.data, in.length);
		talloc_free(mem_ctx);

		return NT_STATUS_INVALID_PARAMETER;
	}

	data_blob_free(&server_domain_blob);

	DEBUG(3, ("Got challenge flags:\n"));
	debug_ntlmssp_flags(chal_flags);

	ntlmssp_handle_neg_flags(ntlmssp_state, chal_flags, ntlmssp_state->allow_lm_key);

	if (ntlmssp_state->unicode) {
		if (chal_flags & NTLMSSP_NEGOTIATE_TARGET_INFO) {
			chal_parse_string = "CdUdbddB";
		} else {
			chal_parse_string = "CdUdbdd";
			chal_parse_string_short = "CdUdb";
		}
		auth_gen_string = "CdBBUUUBdb";
	} else {
		if (chal_flags & NTLMSSP_NEGOTIATE_TARGET_INFO) {
			chal_parse_string = "CdAdbddB";
		} else {
			chal_parse_string = "CdAdbdd";
			chal_parse_string_short = "CdAdb";
		}

		auth_gen_string = "CdBBAAABdb";
	}

	if (!msrpc_parse(mem_ctx,
			 &in, chal_parse_string,
			 "NTLMSSP",
			 &ntlmssp_command,
			 &server_domain,
			 &chal_flags,
			 &challenge_blob, 8,
			 &unkn1, &unkn2,
			 &target_info)) {

		bool ok = false;

		DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n"));

		if (chal_parse_string_short != NULL) {
			/*
			 * In the case where NTLMSSP_NEGOTIATE_TARGET_INFO
			 * is not used, some NTLMSSP servers don't return
			 * the unused unkn1 and unkn2 fields.
			 * See bug:
			 * https://bugzilla.samba.org/show_bug.cgi?id=10016
			 * for packet traces.
			 * Try and parse again without them.
			 */
			ok = msrpc_parse(mem_ctx,
				&in, chal_parse_string_short,
				"NTLMSSP",
				&ntlmssp_command,
				&server_domain,
				&chal_flags,
				&challenge_blob, 8);
			if (!ok) {
				DEBUG(1, ("Failed to short parse "
					"the NTLMSSP Challenge: (#2)\n"));
			}
		}

		if (!ok) {
			dump_data(2, in.data, in.length);
			talloc_free(mem_ctx);
			return NT_STATUS_INVALID_PARAMETER;
		}
	}

	if (chal_flags & NTLMSSP_TARGET_TYPE_SERVER) {
		ntlmssp_state->server.is_standalone = true;
	} else {
		ntlmssp_state->server.is_standalone = false;
	}
	/* TODO: parse struct_blob and fill in the rest */
	ntlmssp_state->server.netbios_name = "";
	ntlmssp_state->server.netbios_domain = talloc_move(ntlmssp_state, &server_domain);
	ntlmssp_state->server.dns_name = "";
	ntlmssp_state->server.dns_domain = "";

	if (challenge_blob.length != 8) {
		talloc_free(mem_ctx);
		return NT_STATUS_INVALID_PARAMETER;
	}

	is_anonymous = cli_credentials_is_anonymous(gensec_security->credentials);
	cli_credentials_get_ntlm_username_domain(gensec_security->credentials, mem_ctx,
						 &user, &domain);

	workstation = cli_credentials_get_workstation(gensec_security->credentials);

	if (user == NULL) {
		DEBUG(10, ("User is NULL, returning INVALID_PARAMETER\n"));
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (domain == NULL) {
		DEBUG(10, ("Domain is NULL, returning INVALID_PARAMETER\n"));
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (workstation == NULL) {
		DEBUG(10, ("Workstation is NULL, returning INVALID_PARAMETER\n"));
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (is_anonymous) {
		ntlmssp_state->neg_flags |= NTLMSSP_ANONYMOUS;
		/*
		 * don't use the ccache for anonymous auth
		 */
		ntlmssp_state->use_ccache = false;
	}
	if (ntlmssp_state->use_ccache) {
		struct samr_Password *nt_hash = NULL;

		/*
		 * If we have a password given we don't
		 * use the ccache
		 */
		nt_hash = cli_credentials_get_nt_hash(gensec_security->credentials,
						      mem_ctx);
		if (nt_hash != NULL) {
			ZERO_STRUCTP(nt_hash);
			TALLOC_FREE(nt_hash);
			ntlmssp_state->use_ccache = false;
		}
	}

	if (ntlmssp_state->use_ccache) {
		struct wbcCredentialCacheParams params;
		struct wbcCredentialCacheInfo *info = NULL;
		struct wbcAuthErrorInfo *error = NULL;
		struct wbcNamedBlob auth_blobs[1];
		const struct wbcBlob *wbc_auth_blob = NULL;
		const struct wbcBlob *wbc_session_key = NULL;
		wbcErr wbc_status;
		int i;

		params.account_name = user;
		params.domain_name = domain;
		params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP;

		auth_blobs[0].name = "challenge_blob";
		auth_blobs[0].flags = 0;
		auth_blobs[0].blob.data = in.data;
		auth_blobs[0].blob.length = in.length;
		params.num_blobs = ARRAY_SIZE(auth_blobs);
		params.blobs = auth_blobs;

		wbc_status = wbcCredentialCache(&params, &info, &error);
		wbcFreeMemory(error);
		if (!WBC_ERROR_IS_OK(wbc_status)) {
			return NT_STATUS_WRONG_CREDENTIAL_HANDLE;
		}

		for (i=0; i<info->num_blobs; i++) {
			if (strequal(info->blobs[i].name, "auth_blob")) {
				wbc_auth_blob = &info->blobs[i].blob;
			}
			if (strequal(info->blobs[i].name, "session_key")) {
				wbc_session_key = &info->blobs[i].blob;
			}
		}
		if ((wbc_auth_blob == NULL) || (wbc_session_key == NULL)) {
			wbcFreeMemory(info);
			return NT_STATUS_WRONG_CREDENTIAL_HANDLE;
		}

		session_key = data_blob_talloc(mem_ctx,
					       wbc_session_key->data,
					       wbc_session_key->length);
		if (session_key.length != wbc_session_key->length) {
			wbcFreeMemory(info);
			return NT_STATUS_NO_MEMORY;
		}
		*out = data_blob_talloc(mem_ctx,
					wbc_auth_blob->data,
					wbc_auth_blob->length);
		if (out->length != wbc_auth_blob->length) {
			wbcFreeMemory(info);
			return NT_STATUS_NO_MEMORY;
		}

		wbcFreeMemory(info);
		goto done;
	}

	if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
		flags |= CLI_CRED_NTLM2;
	}
	if (ntlmssp_state->use_ntlmv2) {
		flags |= CLI_CRED_NTLMv2_AUTH;
	}
	if (ntlmssp_state->use_nt_response) {
		flags |= CLI_CRED_NTLM_AUTH;
	}
	if (lpcfg_client_lanman_auth(gensec_security->settings->lp_ctx)) {
		flags |= CLI_CRED_LANMAN_AUTH;
	}

	nt_status = cli_credentials_get_ntlm_response(gensec_security->credentials, mem_ctx,
						      &flags, challenge_blob, target_info,
						      &lm_response, &nt_response,
						      &lm_session_key, &session_key);

	if (!NT_STATUS_IS_OK(nt_status)) {
		return nt_status;
	}

	if (!(flags & CLI_CRED_LANMAN_AUTH)) {
		/* LM Key is still possible, just silly, so we do not
		 * allow it. Fortunetly all LM crypto is off by
		 * default and we require command line options to end
		 * up here */
		ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
	}

	if (!(flags & CLI_CRED_NTLM2)) {
		/* NTLM2 is incompatible... */
		ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
	}

	if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
	    && lpcfg_client_lanman_auth(gensec_security->settings->lp_ctx) && lm_session_key.length == 16) {
		DATA_BLOB new_session_key = data_blob_talloc(mem_ctx, NULL, 16);
		if (lm_response.length == 24) {
			SMBsesskeygen_lm_sess_key(lm_session_key.data, lm_response.data,
						  new_session_key.data);
		} else {
			static const uint8_t zeros[24];
			SMBsesskeygen_lm_sess_key(lm_session_key.data, zeros,
						  new_session_key.data);
		}
		session_key = new_session_key;
		dump_data_pw("LM session key\n", session_key.data, session_key.length);
	}


	/* Key exchange encryptes a new client-generated session key with
	   the password-derived key */
	if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
		/* Make up a new session key */
		uint8_t client_session_key[16];
		generate_secret_buffer(client_session_key, sizeof(client_session_key));

		/* Encrypt the new session key with the old one */
		encrypted_session_key = data_blob_talloc(ntlmssp_state,
							 client_session_key, sizeof(client_session_key));
		dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length);
		arcfour_crypt(encrypted_session_key.data, session_key.data, encrypted_session_key.length);
		dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length);

		/* Mark the new session key as the 'real' session key */
		session_key = data_blob_talloc(mem_ctx, client_session_key, sizeof(client_session_key));
	}

	/* this generates the actual auth packet */
	nt_status = msrpc_gen(mem_ctx,
		       out, auth_gen_string,
		       "NTLMSSP",
		       NTLMSSP_AUTH,
		       lm_response.data, lm_response.length,
		       nt_response.data, nt_response.length,
		       domain,
		       user,
		       workstation,
		       encrypted_session_key.data, encrypted_session_key.length,
		       ntlmssp_state->neg_flags,
		       version_blob.data, version_blob.length);
	if (!NT_STATUS_IS_OK(nt_status)) {
		talloc_free(mem_ctx);
		return nt_status;
	}

done:
	ntlmssp_state->session_key = session_key;
	talloc_steal(ntlmssp_state, session_key.data);

	DEBUG(3, ("NTLMSSP: Set final flags:\n"));
	debug_ntlmssp_flags(ntlmssp_state->neg_flags);

	talloc_steal(out_mem_ctx, out->data);

	ntlmssp_state->expected_state = NTLMSSP_DONE;

	if (gensec_security->want_features & (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL)) {
		nt_status = ntlmssp_sign_init(ntlmssp_state);
		if (!NT_STATUS_IS_OK(nt_status)) {
			DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n",
				  nt_errstr(nt_status)));
			talloc_free(mem_ctx);
			return nt_status;
		}
	}

	talloc_free(mem_ctx);
	return NT_STATUS_OK;
}
コード例 #2
0
ファイル: ntlmssp_client.c プロジェクト: AIdrifter/samba
NTSTATUS ntlmssp_client_challenge(struct gensec_security *gensec_security,
				  TALLOC_CTX *out_mem_ctx,
				  const DATA_BLOB in, DATA_BLOB *out)
{
	struct gensec_ntlmssp_context *gensec_ntlmssp =
		talloc_get_type_abort(gensec_security->private_data,
				      struct gensec_ntlmssp_context);
	struct ntlmssp_state *ntlmssp_state = gensec_ntlmssp->ntlmssp_state;
	uint32_t chal_flags, ntlmssp_command, unkn1, unkn2;
	DATA_BLOB server_domain_blob;
	DATA_BLOB challenge_blob;
	DATA_BLOB target_info = data_blob(NULL, 0);
	char *server_domain;
	const char *chal_parse_string;
	const char *auth_gen_string;
	DATA_BLOB lm_response = data_blob(NULL, 0);
	DATA_BLOB nt_response = data_blob(NULL, 0);
	DATA_BLOB session_key = data_blob(NULL, 0);
	DATA_BLOB lm_session_key = data_blob(NULL, 0);
	DATA_BLOB encrypted_session_key = data_blob(NULL, 0);
	NTSTATUS nt_status;
	int flags = 0;
	const char *user, *domain;

	TALLOC_CTX *mem_ctx = talloc_new(out_mem_ctx);
	if (!mem_ctx) {
		return NT_STATUS_NO_MEMORY;
	}

	if (!msrpc_parse(mem_ctx,
			 &in, "CdBd",
			 "NTLMSSP",
			 &ntlmssp_command,
			 &server_domain_blob,
			 &chal_flags)) {
		DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#1)\n"));
		dump_data(2, in.data, in.length);
		talloc_free(mem_ctx);

		return NT_STATUS_INVALID_PARAMETER;
	}

	data_blob_free(&server_domain_blob);

	DEBUG(3, ("Got challenge flags:\n"));
	debug_ntlmssp_flags(chal_flags);

	ntlmssp_handle_neg_flags(ntlmssp_state, chal_flags, ntlmssp_state->allow_lm_key);

	if (ntlmssp_state->unicode) {
		if (chal_flags & NTLMSSP_NEGOTIATE_TARGET_INFO) {
			chal_parse_string = "CdUdbddB";
		} else {
			chal_parse_string = "CdUdbdd";
		}
		auth_gen_string = "CdBBUUUBd";
	} else {
		if (chal_flags & NTLMSSP_NEGOTIATE_TARGET_INFO) {
			chal_parse_string = "CdAdbddB";
		} else {
			chal_parse_string = "CdAdbdd";
		}

		auth_gen_string = "CdBBAAABd";
	}

	if (!msrpc_parse(mem_ctx,
			 &in, chal_parse_string,
			 "NTLMSSP",
			 &ntlmssp_command,
			 &server_domain,
			 &chal_flags,
			 &challenge_blob, 8,
			 &unkn1, &unkn2,
			 &target_info)) {
		DEBUG(1, ("Failed to parse the NTLMSSP Challenge: (#2)\n"));
		dump_data(2, in.data, in.length);
		talloc_free(mem_ctx);
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (chal_flags & NTLMSSP_TARGET_TYPE_SERVER) {
		ntlmssp_state->server.is_standalone = true;
	} else {
		ntlmssp_state->server.is_standalone = false;
	}
	/* TODO: parse struct_blob and fill in the rest */
	ntlmssp_state->server.netbios_name = "";
	ntlmssp_state->server.netbios_domain = server_domain;
	ntlmssp_state->server.dns_name = "";
	ntlmssp_state->server.dns_domain = "";

	if (challenge_blob.length != 8) {
		talloc_free(mem_ctx);
		return NT_STATUS_INVALID_PARAMETER;
	}

	cli_credentials_get_ntlm_username_domain(gensec_security->credentials, mem_ctx,
						 &user, &domain);

	if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_NTLM2) {
		flags |= CLI_CRED_NTLM2;
	}
	if (ntlmssp_state->use_ntlmv2) {
		flags |= CLI_CRED_NTLMv2_AUTH;
	}
	if (ntlmssp_state->use_nt_response) {
		flags |= CLI_CRED_NTLM_AUTH;
	}
	if (lpcfg_client_lanman_auth(gensec_security->settings->lp_ctx)) {
		flags |= CLI_CRED_LANMAN_AUTH;
	}

	nt_status = cli_credentials_get_ntlm_response(gensec_security->credentials, mem_ctx,
						      &flags, challenge_blob, target_info,
						      &lm_response, &nt_response,
						      &lm_session_key, &session_key);

	if (!NT_STATUS_IS_OK(nt_status)) {
		return nt_status;
	}

	if (!(flags & CLI_CRED_LANMAN_AUTH)) {
		/* LM Key is still possible, just silly, so we do not
		 * allow it. Fortunetly all LM crypto is off by
		 * default and we require command line options to end
		 * up here */
		ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_LM_KEY;
	}

	if (!(flags & CLI_CRED_NTLM2)) {
		/* NTLM2 is incompatible... */
		ntlmssp_state->neg_flags &= ~NTLMSSP_NEGOTIATE_NTLM2;
	}

	if ((ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_LM_KEY)
	    && lpcfg_client_lanman_auth(gensec_security->settings->lp_ctx) && lm_session_key.length == 16) {
		DATA_BLOB new_session_key = data_blob_talloc(mem_ctx, NULL, 16);
		if (lm_response.length == 24) {
			SMBsesskeygen_lm_sess_key(lm_session_key.data, lm_response.data,
						  new_session_key.data);
		} else {
			static const uint8_t zeros[24];
			SMBsesskeygen_lm_sess_key(lm_session_key.data, zeros,
						  new_session_key.data);
		}
		session_key = new_session_key;
		dump_data_pw("LM session key\n", session_key.data, session_key.length);
	}


	/* Key exchange encryptes a new client-generated session key with
	   the password-derived key */
	if (ntlmssp_state->neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
		/* Make up a new session key */
		uint8_t client_session_key[16];
		generate_secret_buffer(client_session_key, sizeof(client_session_key));

		/* Encrypt the new session key with the old one */
		encrypted_session_key = data_blob_talloc(ntlmssp_state,
							 client_session_key, sizeof(client_session_key));
		dump_data_pw("KEY_EXCH session key:\n", encrypted_session_key.data, encrypted_session_key.length);
		arcfour_crypt(encrypted_session_key.data, session_key.data, encrypted_session_key.length);
		dump_data_pw("KEY_EXCH session key (enc):\n", encrypted_session_key.data, encrypted_session_key.length);

		/* Mark the new session key as the 'real' session key */
		session_key = data_blob_talloc(mem_ctx, client_session_key, sizeof(client_session_key));
	}

	DEBUG(3, ("NTLMSSP: Set final flags:\n"));
	debug_ntlmssp_flags(ntlmssp_state->neg_flags);

	/* this generates the actual auth packet */
	nt_status = msrpc_gen(mem_ctx,
		       out, auth_gen_string,
		       "NTLMSSP",
		       NTLMSSP_AUTH,
		       lm_response.data, lm_response.length,
		       nt_response.data, nt_response.length,
		       domain,
		       user,
		       cli_credentials_get_workstation(gensec_security->credentials),
		       encrypted_session_key.data, encrypted_session_key.length,
		       ntlmssp_state->neg_flags);
	if (!NT_STATUS_IS_OK(nt_status)) {
		talloc_free(mem_ctx);
		return nt_status;
	}

	ntlmssp_state->session_key = session_key;
	talloc_steal(ntlmssp_state, session_key.data);

	talloc_steal(out_mem_ctx, out->data);

	ntlmssp_state->expected_state = NTLMSSP_DONE;

	if (gensec_security->want_features & (GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL)) {
		nt_status = ntlmssp_sign_init(ntlmssp_state);
		if (!NT_STATUS_IS_OK(nt_status)) {
			DEBUG(1, ("Could not setup NTLMSSP signing/sealing system (error was: %s)\n",
				  nt_errstr(nt_status)));
			talloc_free(mem_ctx);
			return nt_status;
		}
	}

	talloc_free(mem_ctx);
	return NT_STATUS_OK;
}
コード例 #3
0
ファイル: trusts_util.c プロジェクト: DanilKorotenko/samba
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 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;
	}

	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);

	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;
	}

	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;
	}

	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);

	new_version = old_version + 1;

	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;
		}

		new_trust_version = &new_version;
		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
	 * 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;
	}

	/*
	 * 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 for domain %s - %s!\n",
			  domain, nt_errstr(status)));
		TALLOC_FREE(frame);
		return status;
	}

	/*
	 * 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;

	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;

	default:
		smb_panic("Unsupported secure channel type");
		break;
	}

	DEBUG(1,("%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 failed - %s\n",
			 current_timestring(talloc_tos(), false), __func__,
			 domain, nt_errstr(status)));
		TALLOC_FREE(frame);
		return status;
	}

	DEBUG(1,("%s : %s(%s): Changed password remotely.\n",
		 current_timestring(talloc_tos(), false), __func__, domain));

	TALLOC_FREE(frame);
	return NT_STATUS_OK;
}