enum samr_ValidationStatus samdb_check_password(TALLOC_CTX *mem_ctx,
						struct loadparm_context *lp_ctx,
						const char *account_name,
						const char *user_principal_name,
						const char *full_name,
						const DATA_BLOB *utf8_blob,
						const uint32_t pwdProperties,
						const uint32_t minPwdLength)
{
	const struct loadparm_substitution *lp_sub =
		lpcfg_noop_substitution();
	char *password_script = NULL;
	const char *utf8_pw = (const char *)utf8_blob->data;

	/*
	 * This looks strange because it is.
	 *
	 * The check for the number of characters in the password
	 * should clearly not be against the byte length, or else a
	 * single UTF8 character would count for more than one.
	 *
	 * We have chosen to use the number of 16-bit units that the
	 * password encodes to as the measure of length.  This is not
	 * the same as the number of codepoints, if a password
	 * contains a character beyond the Basic Multilingual Plane
	 * (above 65535) it will count for more than one "character".
	 */

	size_t password_characters_roughly = strlen_m(utf8_pw);

	/* checks if the "minPwdLength" property is satisfied */
	if (minPwdLength > password_characters_roughly) {
		return SAMR_VALIDATION_STATUS_PWD_TOO_SHORT;
	}

	/* We might not be asked to check the password complexity */
	if (!(pwdProperties & DOMAIN_PASSWORD_COMPLEX)) {
		return SAMR_VALIDATION_STATUS_SUCCESS;
	}

	if (password_characters_roughly == 0) {
		return SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH;
	}

	password_script = lpcfg_check_password_script(lp_ctx, lp_sub, mem_ctx);
	if (password_script != NULL && *password_script != '\0') {
		int check_ret = 0;
		int error = 0;
		ssize_t nwritten = 0;
		struct tevent_context *event_ctx = NULL;
		struct tevent_req *req = NULL;
		int cps_stdin = -1;
		const char * const cmd[4] = {
			"/bin/sh", "-c",
			password_script,
			NULL
		};

		event_ctx = tevent_context_init(mem_ctx);
		if (event_ctx == NULL) {
			TALLOC_FREE(password_script);
			return SAMR_VALIDATION_STATUS_PASSWORD_FILTER_ERROR;
		}

		/* Gives a warning after 1 second, terminates after 10 */
		tevent_add_timer(event_ctx, event_ctx,
				 tevent_timeval_current_ofs(1, 0),
				 pwd_timeout_debug, NULL);

		check_ret = setenv("SAMBA_CPS_ACCOUNT_NAME", account_name, 1);
		if (check_ret != 0) {
			TALLOC_FREE(password_script);
			TALLOC_FREE(event_ctx);
			return SAMR_VALIDATION_STATUS_PASSWORD_FILTER_ERROR;
		}
		if (user_principal_name != NULL) {
			check_ret = setenv("SAMBA_CPS_USER_PRINCIPAL_NAME",
					   user_principal_name, 1);
		} else {
			unsetenv("SAMBA_CPS_USER_PRINCIPAL_NAME");
		}
		if (check_ret != 0) {
			TALLOC_FREE(password_script);
			TALLOC_FREE(event_ctx);
			return SAMR_VALIDATION_STATUS_PASSWORD_FILTER_ERROR;
		}
		if (full_name != NULL) {
			check_ret = setenv("SAMBA_CPS_FULL_NAME", full_name, 1);
		} else {
			unsetenv("SAMBA_CPS_FULL_NAME");
		}
		if (check_ret != 0) {
			TALLOC_FREE(password_script);
			TALLOC_FREE(event_ctx);
			return SAMR_VALIDATION_STATUS_PASSWORD_FILTER_ERROR;
		}

		req = samba_runcmd_send(event_ctx, event_ctx,
					tevent_timeval_current_ofs(10, 0),
					100, 100, cmd, NULL);
		unsetenv("SAMBA_CPS_ACCOUNT_NAME");
		unsetenv("SAMBA_CPS_USER_PRINCIPAL_NAME");
		unsetenv("SAMBA_CPS_FULL_NAME");
		if (req == NULL) {
			TALLOC_FREE(password_script);
			TALLOC_FREE(event_ctx);
			return SAMR_VALIDATION_STATUS_PASSWORD_FILTER_ERROR;
		}

		cps_stdin = samba_runcmd_export_stdin(req);

		nwritten = write_data(
			cps_stdin, utf8_blob->data, utf8_blob->length);
		if (nwritten == -1) {
			close(cps_stdin);
			TALLOC_FREE(password_script);
			TALLOC_FREE(event_ctx);
			return SAMR_VALIDATION_STATUS_PASSWORD_FILTER_ERROR;
		}

		close(cps_stdin);

		if (!tevent_req_poll(req, event_ctx)) {
			TALLOC_FREE(password_script);
			TALLOC_FREE(event_ctx);
			return SAMR_VALIDATION_STATUS_PASSWORD_FILTER_ERROR;
		}

		check_ret = samba_runcmd_recv(req, &error);
		TALLOC_FREE(event_ctx);

		if (error == ETIMEDOUT) {
			DEBUG(0, ("check_password_complexity: check password script took too long!\n"));
			TALLOC_FREE(password_script);
			return SAMR_VALIDATION_STATUS_PASSWORD_FILTER_ERROR;
		}
		DEBUG(5,("check_password_complexity: check password script (%s) "
			 "returned [%d]\n", password_script, check_ret));

		if (check_ret != 0) {
			DEBUG(1,("check_password_complexity: "
				 "check password script said new password is not good "
				 "enough!\n"));
			TALLOC_FREE(password_script);
			return SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH;
		}

		TALLOC_FREE(password_script);
		return SAMR_VALIDATION_STATUS_SUCCESS;
	}

	TALLOC_FREE(password_script);

	/*
	 * Here are the standard AD password quality rules, which we
	 * run after the script.
	 */

	if (!check_password_quality(utf8_pw)) {
		return SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH;
	}

	return SAMR_VALIDATION_STATUS_SUCCESS;
}