static int descriptor_sd_propagation_object(struct ldb_module *module,
					    struct ldb_message *msg,
					    bool *stop)
{
	struct descriptor_data *descriptor_private =
		talloc_get_type_abort(ldb_module_get_private(module),
		struct descriptor_data);
	struct descriptor_transaction *t = &descriptor_private->transaction;
	struct ldb_context *ldb = ldb_module_get_ctx(module);
	struct ldb_request *sub_req;
	struct ldb_result *mod_res;
	struct ldb_control *sd_propagation_control;
	struct GUID guid;
	int ret;
	TDB_DATA key;
	TDB_DATA empty_val = { .dsize = 0, };
	NTSTATUS status;
	struct descriptor_changes *c = NULL;

	*stop = false;

	/*
	 * We get the GUID of the object
	 * in order to have the cache key
	 * for the object.
	 */

	status = dsdb_get_extended_dn_guid(msg->dn, &guid, "GUID");
	if (!NT_STATUS_IS_OK(status)) {
		return ldb_operr(ldb);
	}
	key = make_tdb_data((const void*)&guid, sizeof(guid));

	/*
	 * Check if we already processed this object.
	 */
	status = dbwrap_parse_record(t->objects.map, key,
				     descriptor_object_parser, NULL);
	if (NT_STATUS_IS_OK(status)) {
		/*
		 * All work is already one
		 */
		t->objects.num_skipped += 1;
		*stop = true;
		return LDB_SUCCESS;
	}
	if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
		ldb_debug(ldb, LDB_DEBUG_FATAL,
			  "dbwrap_parse_record() - %s\n",
			  nt_errstr(status));
		return ldb_module_operr(module);
	}

	t->objects.num_processed += 1;

	/*
	 * Remember that we're processing this object.
	 */
	status = dbwrap_store(t->objects.map, key, empty_val, TDB_INSERT);
	if (!NT_STATUS_IS_OK(status)) {
		ldb_debug(ldb, LDB_DEBUG_FATAL,
			  "dbwrap_parse_record() - %s\n",
			  nt_errstr(status));
		return ldb_module_operr(module);
	}

	/*
	 * Check that if there's a descriptor_change in our list,
	 * which we may be able to remove from the pending list
	 * when we processed the object.
	 */

	status = dbwrap_parse_record(t->changes.map, key, descriptor_changes_parser, &c);
	if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
		c = NULL;
		status = NT_STATUS_OK;
	}
	if (!NT_STATUS_IS_OK(status)) {
		ldb_debug(ldb, LDB_DEBUG_FATAL,
			  "dbwrap_parse_record() - %s\n",
			  nt_errstr(status));
		return ldb_module_operr(module);
	}

	mod_res = talloc_zero(msg, struct ldb_result);
	if (mod_res == NULL) {
		return ldb_module_oom(module);
	}

	ret = ldb_build_mod_req(&sub_req, ldb, mod_res,
				msg,
				NULL,
				mod_res,
				ldb_modify_default_callback,
				NULL);
	LDB_REQ_SET_LOCATION(sub_req);
	if (ret != LDB_SUCCESS) {
		return ldb_module_operr(module);
	}

	ldb_req_mark_trusted(sub_req);

	ret = ldb_request_add_control(sub_req,
				      DSDB_CONTROL_SEC_DESC_PROPAGATION_OID,
				      true, module);
	if (ret != LDB_SUCCESS) {
		return ldb_module_operr(module);
	}

	sd_propagation_control = ldb_request_get_control(sub_req,
					DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
	if (sd_propagation_control == NULL) {
		return ldb_module_operr(module);
	}

	ret = dsdb_request_add_controls(sub_req,
					DSDB_FLAG_AS_SYSTEM |
					DSDB_SEARCH_SHOW_RECYCLED);
	if (ret != LDB_SUCCESS) {
		return ldb_module_operr(module);
	}

	ret = descriptor_modify(module, sub_req);
	if (ret == LDB_SUCCESS) {
		ret = ldb_wait(sub_req->handle, LDB_WAIT_ALL);
	}
	if (ret != LDB_SUCCESS) {
		ldb_asprintf_errstring(ldb_module_get_ctx(module),
				       "descriptor_modify on %s failed: %s",
				       ldb_dn_get_linearized(msg->dn),
				       ldb_errstring(ldb_module_get_ctx(module)));
		return LDB_ERR_OPERATIONS_ERROR;
	}

	if (sd_propagation_control->critical != 0) {
		if (c == NULL) {
			/*
			 * If we don't have a
			 * descriptor_changes structure
			 * we're done.
			 */
			*stop = true;
		} else if (!c->force_children) {
			/*
			 * If we don't need to
			 * propagate to children,
			 * we're done.
			 */
			*stop = true;
		}
	}

	if (c != NULL && !c->force_children) {
		/*
		 * Remove the pending change,
		 * we already done all required work,
		 * there's no need to do it again.
		 *
		 * Note DLIST_REMOVE() is a noop
		 * if the element is not part of
		 * the list.
		 */
		DLIST_REMOVE(t->changes.list, c);
	}

	talloc_free(mod_res);

	return LDB_SUCCESS;
}