static NTSTATUS samdb_set_password_internal(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
			    struct ldb_dn *user_dn, struct ldb_dn *domain_dn,
			    const DATA_BLOB *new_password,
			    const struct samr_Password *ntNewHash,
			    enum dsdb_password_checked old_password_checked,
			    enum samPwdChangeReason *reject_reason,
			    struct samr_DomInfo1 **_dominfo,
			    bool permit_interdomain_trust)
{
	struct ldb_message *msg;
	struct ldb_message_element *el;
	struct ldb_request *req;
	struct dsdb_control_password_change_status *pwd_stat = NULL;
	int ret;
	bool hash_values = false;
	NTSTATUS status = NT_STATUS_OK;

#define CHECK_RET(x) \
	if (x != LDB_SUCCESS) { \
		talloc_free(msg); \
		return NT_STATUS_NO_MEMORY; \
	}

	msg = ldb_msg_new(mem_ctx);
	if (msg == NULL) {
		return NT_STATUS_NO_MEMORY;
	}
	msg->dn = user_dn;
	if ((new_password != NULL)
			&& ((ntNewHash == NULL))) {
		/* we have the password as plaintext UTF16 */
		CHECK_RET(ldb_msg_add_value(msg, "clearTextPassword",
					    new_password, NULL));
		el = ldb_msg_find_element(msg, "clearTextPassword");
		el->flags = LDB_FLAG_MOD_REPLACE;
	} else if ((new_password == NULL)
			&& ((ntNewHash != NULL))) {
		/* we have a password as NT hash */
		if (ntNewHash != NULL) {
			CHECK_RET(samdb_msg_add_hash(ldb, mem_ctx, msg,
				"unicodePwd", ntNewHash));
			el = ldb_msg_find_element(msg, "unicodePwd");
			el->flags = LDB_FLAG_MOD_REPLACE;
		}
		hash_values = true;
	} else {
		/* the password wasn't specified correctly */
		talloc_free(msg);
		return NT_STATUS_INVALID_PARAMETER;
	}

	/* build modify request */
	ret = ldb_build_mod_req(&req, ldb, mem_ctx, msg, NULL, NULL,
				samdb_set_password_callback, NULL);
        if (ret != LDB_SUCCESS) {
		talloc_free(msg);
		return NT_STATUS_NO_MEMORY;
        }

	/* A password change operation */
	if (old_password_checked == DSDB_PASSWORD_CHECKED_AND_CORRECT) {
		struct dsdb_control_password_change *change;

		change = talloc(req, struct dsdb_control_password_change);
		if (change == NULL) {
			talloc_free(req);
			talloc_free(msg);
			return NT_STATUS_NO_MEMORY;
		}

		change->old_password_checked = old_password_checked;

		ret = ldb_request_add_control(req,
					      DSDB_CONTROL_PASSWORD_CHANGE_OLD_PW_CHECKED_OID,
					      true, change);
		if (ret != LDB_SUCCESS) {
			talloc_free(req);
			talloc_free(msg);
			return NT_STATUS_NO_MEMORY;
		}
	}
	if (hash_values) {
		ret = ldb_request_add_control(req,
					      DSDB_CONTROL_PASSWORD_HASH_VALUES_OID,
					      true, NULL);
		if (ret != LDB_SUCCESS) {
			talloc_free(req);
			talloc_free(msg);
			return NT_STATUS_NO_MEMORY;
		}
	}
	if (permit_interdomain_trust) {
		ret = ldb_request_add_control(req,
					      DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID,
					      false, NULL);
		if (ret != LDB_SUCCESS) {
			talloc_free(req);
			talloc_free(msg);
			return NT_STATUS_NO_MEMORY;
		}
	}
	ret = ldb_request_add_control(req,
				      DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
				      true, NULL);
	if (ret != LDB_SUCCESS) {
		talloc_free(req);
		talloc_free(msg);
		return NT_STATUS_NO_MEMORY;
	}

	ret = dsdb_autotransaction_request(ldb, req);

	if (req->context != NULL) {
		struct ldb_control *control = talloc_get_type_abort(req->context,
								    struct ldb_control);
		pwd_stat = talloc_get_type_abort(control->data,
						 struct dsdb_control_password_change_status);
		talloc_steal(mem_ctx, pwd_stat);
	}

	talloc_free(req);
	talloc_free(msg);

	/* Sets the domain info (if requested) */
	if (_dominfo != NULL) {
		struct samr_DomInfo1 *dominfo;

		dominfo = talloc_zero(mem_ctx, struct samr_DomInfo1);
		if (dominfo == NULL) {
			return NT_STATUS_NO_MEMORY;
		}

		if (pwd_stat != NULL) {
			dominfo->min_password_length     = pwd_stat->domain_data.minPwdLength;
			dominfo->password_properties     = pwd_stat->domain_data.pwdProperties;
			dominfo->password_history_length = pwd_stat->domain_data.pwdHistoryLength;
			dominfo->max_password_age        = pwd_stat->domain_data.maxPwdAge;
			dominfo->min_password_age        = pwd_stat->domain_data.minPwdAge;
		}

		*_dominfo = dominfo;
	}

	if (reject_reason != NULL) {
		if (pwd_stat != NULL) {
			*reject_reason = pwd_stat->reject_reason;
		} else {
			*reject_reason = SAM_PWD_CHANGE_NO_ERROR;
		}
	}

	if (pwd_stat != NULL) {
		talloc_free(pwd_stat);
	}

	if (ret == LDB_ERR_CONSTRAINT_VIOLATION) {
		const char *errmsg = ldb_errstring(ldb);
		char *endptr = NULL;
		WERROR werr = WERR_GEN_FAILURE;
		status = NT_STATUS_UNSUCCESSFUL;
		if (errmsg != NULL) {
			werr = W_ERROR(strtol(errmsg, &endptr, 16));
			DBG_WARNING("%s\n", errmsg);
		}
		if (endptr != errmsg) {
			if (W_ERROR_EQUAL(werr, WERR_INVALID_PASSWORD)) {
				status = NT_STATUS_WRONG_PASSWORD;
			}
			if (W_ERROR_EQUAL(werr, WERR_PASSWORD_RESTRICTION)) {
				status = NT_STATUS_PASSWORD_RESTRICTION;
			}
		}
	} else if (ret == LDB_ERR_NO_SUCH_OBJECT) {
		/* don't let the caller know if an account doesn't exist */
		status = NT_STATUS_WRONG_PASSWORD;
	} else if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) {
		status = NT_STATUS_ACCESS_DENIED;
	} else if (ret != LDB_SUCCESS) {
		DEBUG(1, ("Failed to set password on %s: %s\n",
			  ldb_dn_get_linearized(user_dn),
			  ldb_errstring(ldb)));
		status = NT_STATUS_UNSUCCESSFUL;
	}

	return status;
}