static int samldb_modify(struct ldb_module *module, struct ldb_request *req)
{
	struct ldb_context *ldb;
	struct samldb_ctx *ac;
	struct ldb_message_element *el, *el2;
	struct ldb_control *is_undelete;
	bool modified = false;
	int ret;

	if (ldb_dn_is_special(req->op.mod.message->dn)) {
		/* do not manipulate our control entries */
		return ldb_next_request(module, req);
	}

	ldb = ldb_module_get_ctx(module);

	/*
	 * we are going to need some special handling if in Undelete call.
	 * Since tombstone_reanimate module will restore certain attributes,
	 * we need to relax checks for: sAMAccountType, primaryGroupID
	 */
	is_undelete = ldb_request_get_control(req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID);

	/* make sure that "objectSid" is not specified */
	el = ldb_msg_find_element(req->op.mod.message, "objectSid");
	if (el != NULL) {
		if (ldb_request_get_control(req, LDB_CONTROL_PROVISION_OID) == NULL) {
			ldb_set_errstring(ldb,
					  "samldb: objectSid must not be specified!");
			return LDB_ERR_UNWILLING_TO_PERFORM;
		}
	}
	if (is_undelete == NULL) {
		/* make sure that "sAMAccountType" is not specified */
		el = ldb_msg_find_element(req->op.mod.message, "sAMAccountType");
		if (el != NULL) {
			ldb_set_errstring(ldb,
					  "samldb: sAMAccountType must not be specified!");
			return LDB_ERR_UNWILLING_TO_PERFORM;
		}
	}
	/* make sure that "isCriticalSystemObject" is not specified */
	el = ldb_msg_find_element(req->op.mod.message, "isCriticalSystemObject");
	if (el != NULL) {
		if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID) == NULL) {
			ldb_set_errstring(ldb,
					  "samldb: isCriticalSystemObject must not be specified!");
			return LDB_ERR_UNWILLING_TO_PERFORM;
		}
	}

	/* msDS-IntId is not allowed to be modified
	 * except when modification comes from replication */
	if (ldb_msg_find_element(req->op.mod.message, "msDS-IntId")) {
		if (!ldb_request_get_control(req,
					     DSDB_CONTROL_REPLICATED_UPDATE_OID)) {
			return LDB_ERR_CONSTRAINT_VIOLATION;
		}
	}

	el = ldb_msg_find_element(req->op.mod.message, "userParameters");
	if (el != NULL && ldb_req_is_untrusted(req)) {
		const char *reason = "samldb: "
			"setting userParameters is not supported over LDAP, "
			"see https://bugzilla.samba.org/show_bug.cgi?id=8077";
		ldb_debug(ldb, LDB_DEBUG_WARNING, "%s", reason);
		return ldb_error(ldb, LDB_ERR_CONSTRAINT_VIOLATION, reason);
	}

	ac = samldb_ctx_init(module, req);
	if (ac == NULL) {
		return ldb_operr(ldb);
	}

	/* build the new msg */
	ac->msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
	if (ac->msg == NULL) {
		talloc_free(ac);
		ldb_debug(ldb, LDB_DEBUG_FATAL,
			  "samldb_modify: ldb_msg_copy_shallow failed!\n");
		return ldb_operr(ldb);
	}

	ret = samldb_check_sensitive_attributes(ac);
	if (ret != LDB_SUCCESS) {
		talloc_free(ac);
		return ret;
	}

	if (is_undelete == NULL) {
		el = ldb_msg_find_element(ac->msg, "primaryGroupID");
		if (el != NULL) {
			ret = samldb_prim_group_trigger(ac);
			if (ret != LDB_SUCCESS) {
				return ret;
			}
		}
	}

	el = ldb_msg_find_element(ac->msg, "userAccountControl");
	if (el != NULL) {
		modified = true;
		ret = samldb_user_account_control_change(ac);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
	}

	el = ldb_msg_find_element(ac->msg, "pwdLastSet");
	if (el != NULL) {
		modified = true;
		ret = samldb_pwd_last_set_change(ac);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
	}

	el = ldb_msg_find_element(ac->msg, "lockoutTime");
	if (el != NULL) {
		modified = true;
		ret = samldb_lockout_time(ac);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
	}

	el = ldb_msg_find_element(ac->msg, "groupType");
	if (el != NULL) {
		modified = true;
		ret = samldb_group_type_change(ac);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
	}

	el = ldb_msg_find_element(ac->msg, "sAMAccountName");
	if (el != NULL) {
		uint32_t user_account_control;
		struct ldb_result *res = NULL;
		const char * const attrs[] = { "userAccountControl",
					       "objectclass",
					       NULL };
		ret = dsdb_module_search_dn(ac->module,
					    ac,
					    &res,
					    ac->msg->dn,
					    attrs,
					    DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED,
					    ac->req);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
		user_account_control
			= ldb_msg_find_attr_as_uint(res->msgs[0],
						    "userAccountControl",
						    0);

		if ((user_account_control
		     & UF_TRUST_ACCOUNT_MASK) != 0) {
			ac->need_trailing_dollar = true;

		} else if (samdb_find_attribute(ldb,
						res->msgs[0],
						"objectclass",
						"computer")
			   != NULL) {
			ac->need_trailing_dollar = true;
		}

		ret = samldb_sam_accountname_valid_check(ac);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
	}

	el = ldb_msg_find_element(ac->msg, "userPrincipalName");
	if (el != NULL) {
		ret = samldb_sam_account_upn_clash(ac);
		if (ret != LDB_SUCCESS) {
			talloc_free(ac);
			return ret;
		}
	}

	el = ldb_msg_find_element(ac->msg, "ldapDisplayName");
	if (el != NULL) {
		ret = samldb_schema_ldapdisplayname_valid_check(ac);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
	}

	el = ldb_msg_find_element(ac->msg, "attributeID");
	if (el != NULL) {
		ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
				       "Once set, attributeID values may not be modified");
		return LDB_ERR_CONSTRAINT_VIOLATION;
	}

	el = ldb_msg_find_element(ac->msg, "governsID");
	if (el != NULL) {
		ldb_asprintf_errstring(ldb_module_get_ctx(ac->module),
				       "Once set, governsID values may not be modified");
		return LDB_ERR_CONSTRAINT_VIOLATION;
	}

	el = ldb_msg_find_element(ac->msg, "member");
	if (el != NULL) {
		struct ldb_control *fix_link_sid_ctrl = NULL;

		fix_link_sid_ctrl = ldb_request_get_control(ac->req,
					DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
		if (fix_link_sid_ctrl == NULL) {
			ret = samldb_member_check(ac);
			if (ret != LDB_SUCCESS) {
				return ret;
			}
		}
	}

	el = ldb_msg_find_element(ac->msg, "description");
	if (el != NULL) {
		ret = samldb_description_check(ac, &modified);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
	}

	el = ldb_msg_find_element(ac->msg, "dNSHostName");
	el2 = ldb_msg_find_element(ac->msg, "sAMAccountName");
	if ((el != NULL) || (el2 != NULL)) {
		modified = true;
		/*
		 * samldb_service_principal_names_change() might add SPN
		 * changes to the request, so this must come before the SPN
		 * uniqueness check below.
		 *
		 * Note we ALSO have to do the SPN uniqueness check inside
		 * samldb_service_principal_names_change(), because it does a
		 * subrequest to do requested SPN modifications *before* its
		 * automatic ones are added.
		 */
		ret = samldb_service_principal_names_change(ac);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
	}

	el = ldb_msg_find_element(ac->msg, "servicePrincipalName");
	if ((el != NULL)) {
		/*
		 * We need to check whether the SPN collides with an existing
		 * one (anywhere) including via aliases.
		 */
		modified = true;
		ret = samldb_spn_uniqueness_check(ac, el);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
	}

	el = ldb_msg_find_element(ac->msg, "fSMORoleOwner");
	if (el != NULL) {
		ret = samldb_fsmo_role_owner_check(ac);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
	}

	if (modified) {
		struct ldb_request *child_req;

		/* Now perform the real modifications as a child request */
		ret = ldb_build_mod_req(&child_req, ldb, ac,
					ac->msg,
					req->controls,
					req, dsdb_next_callback,
					req);
		LDB_REQ_SET_LOCATION(child_req);
		if (ret != LDB_SUCCESS) {
			return ret;
		}

		return ldb_next_request(module, child_req);
	}

	talloc_free(ac);

	/* no change which interests us, go on */
	return ldb_next_request(module, req);
}