Пример #1
0
static int acl_childClasses(struct ldb_module *module,
			    struct ldb_message *sd_msg,
			    struct ldb_message *msg,
			    const char *attrName)
{
	struct ldb_message_element *oc_el;
	struct ldb_message_element *allowedClasses;
	struct ldb_context *ldb = ldb_module_get_ctx(module);
	const struct dsdb_schema *schema = dsdb_get_schema(ldb);
	const struct dsdb_class *sclass;
	int i, j, ret;

	/* If we don't have a schema yet, we can't do anything... */
	if (schema == NULL) {
		return LDB_SUCCESS;
	}

	/* Must remove any existing attribute, or else confusion reins */
	ldb_msg_remove_attr(msg, attrName);
	ret = ldb_msg_add_empty(msg, attrName, 0, &allowedClasses);
	if (ret != LDB_SUCCESS) {
		return ret;
	}

	oc_el = ldb_msg_find_element(sd_msg, "objectClass");

	for (i=0; oc_el && i < oc_el->num_values; i++) {
		sclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &oc_el->values[i]);
		if (!sclass) {
			/* We don't know this class?  what is going on? */
			continue;
		}

		for (j=0; sclass->possibleInferiors && sclass->possibleInferiors[j]; j++) {
			ldb_msg_add_string(msg, attrName, sclass->possibleInferiors[j]);
		}
	}
	if (allowedClasses->num_values > 1) {
		qsort(allowedClasses->values,
		      allowedClasses->num_values,
		      sizeof(*allowedClasses->values),
		      (comparison_fn_t)data_blob_cmp);

		for (i=1 ; i < allowedClasses->num_values; i++) {
			struct ldb_val *val1 = &allowedClasses->values[i-1];
			struct ldb_val *val2 = &allowedClasses->values[i];
			if (data_blob_cmp(val1, val2) == 0) {
				memmove(val1, val2, (allowedClasses->num_values - i) * sizeof(struct ldb_val));
				allowedClasses->num_values--;
				i--;
			}
		}
	}

	return LDB_SUCCESS;
}
Пример #2
0
static int inject_extended_dn_out(struct ldb_reply *ares,
				  struct ldb_context *ldb,
				  int type,
				  bool remove_guid,
				  bool remove_sid)
{
	int ret;
	const DATA_BLOB *guid_blob;
	const DATA_BLOB *sid_blob;

	guid_blob = ldb_msg_find_ldb_val(ares->message, "objectGUID");
	sid_blob = ldb_msg_find_ldb_val(ares->message, "objectSID");

	if (!guid_blob) {
		ldb_set_errstring(ldb, "Did not find objectGUID to inject into extended DN");
		return LDB_ERR_OPERATIONS_ERROR;
	}

	ret = ldb_dn_set_extended_component(ares->message->dn, "GUID", guid_blob);
	if (ret != LDB_SUCCESS) {
		return ret;
	}
	if (sid_blob) {
		ret = ldb_dn_set_extended_component(ares->message->dn, "SID", sid_blob);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
	}

	if (remove_guid) {
		ldb_msg_remove_attr(ares->message, "objectGUID");
	}

	if (sid_blob && remove_sid) {
		ldb_msg_remove_attr(ares->message, "objectSID");
	}

	return LDB_SUCCESS;
}
Пример #3
0
/*
  post process a search result record. For any search_sub[] attributes that were
  asked for, we need to call the appropriate copy routine to copy the result
  into the message, then remove any attributes that we added to the search but
  were not asked for by the user
*/
static int operational_search_post_process(struct ldb_module *module,
					   struct ldb_message *msg,
					   const char * const *attrs)
{
	struct ldb_context *ldb;
	int i, a=0;

	ldb = ldb_module_get_ctx(module);

	for (a=0;attrs && attrs[a];a++) {
		for (i=0;i<ARRAY_SIZE(search_sub);i++) {
			if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
				continue;
			}

			/* construct the new attribute, using either a supplied
			   constructor or a simple copy */
			if (search_sub[i].constructor) {
				if (search_sub[i].constructor(module, msg) != 0) {
					goto failed;
				}
			} else if (ldb_msg_copy_attr(msg,
						     search_sub[i].replace,
						     search_sub[i].attr) != 0) {
				goto failed;
			}

			/* remove the added search attribute, unless it was
 			   asked for by the user */
			if (search_sub[i].replace == NULL ||
			    ldb_attr_in_list(attrs, search_sub[i].replace) ||
			    ldb_attr_in_list(attrs, "*")) {
				continue;
			}

			ldb_msg_remove_attr(msg, search_sub[i].replace);
		}
	}

	return 0;

failed:
	ldb_debug_set(ldb, LDB_DEBUG_WARNING,
		      "operational_search_post_process failed for attribute '%s'",
		      attrs[a]);
	return -1;
}
Пример #4
0
static int acl_sDRightsEffective(struct ldb_module *module,
				 struct ldb_message *sd_msg,
				 struct ldb_message *msg,
				 struct acl_context *ac)
{
	struct ldb_message_element *rightsEffective;
	int ret;
	struct security_descriptor *sd;
	struct dom_sid *sid = NULL;
	uint32_t flags = 0;

	/* Must remove any existing attribute, or else confusion reins */
	ldb_msg_remove_attr(msg, "sDRightsEffective");
	ret = ldb_msg_add_empty(msg, "sDRightsEffective", 0, &rightsEffective);
	if (ret != LDB_SUCCESS) {
		return ret;
	}
	if (ac->user_type == SECURITY_SYSTEM) {
		flags = SECINFO_OWNER | SECINFO_GROUP |  SECINFO_SACL |  SECINFO_DACL;
	}
	else {
		/* Get the security descriptor from the message */
		ret = get_sd_from_ldb_message(msg, sd_msg, &sd);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
		ret = get_dom_sid_from_ldb_message(msg, sd_msg, &sid);

		if (ret != LDB_SUCCESS) {
			return ret;
		}
		ret = acl_check_access_on_attribute(module,
						    msg,
						    sd,
						    sid,
						    SEC_STD_WRITE_OWNER,
						    NULL);
		if (ret == LDB_SUCCESS) {
			flags |= SECINFO_OWNER | SECINFO_GROUP;
		}
		ret = acl_check_access_on_attribute(module,
						    msg,
						    sd,
						    sid,
						    SEC_STD_WRITE_DAC,
						    NULL);
		if (ret == LDB_SUCCESS) {
			flags |= SECINFO_DACL;
		}
		ret = acl_check_access_on_attribute(module,
						    msg,
						    sd,
						    sid,
						    SEC_FLAG_SYSTEM_SECURITY,
						    NULL);
		if (ret == LDB_SUCCESS) {
			flags |= SECINFO_SACL;
		}
	}
	ldb_msg_add_fmt(msg, "sDRightsEffective", "%u", flags);
	return LDB_SUCCESS;
}
Пример #5
0
static int acl_childClassesEffective(struct ldb_module *module,
				     struct ldb_message *sd_msg,
				     struct ldb_message *msg,
				     struct acl_context *ac)
{
	struct ldb_message_element *oc_el;
	struct ldb_message_element *allowedClasses = NULL;
	struct ldb_context *ldb = ldb_module_get_ctx(module);
	const struct dsdb_schema *schema = dsdb_get_schema(ldb);
	const struct dsdb_class *sclass;
	struct security_descriptor *sd;
	struct dom_sid *sid = NULL;
	int i, j, ret;

	if (ac->user_type == SECURITY_SYSTEM) {
		return acl_childClasses(module, sd_msg, msg, "allowedChildClassesEffective");
	}

	/* If we don't have a schema yet, we can't do anything... */
	if (schema == NULL) {
		return LDB_SUCCESS;
	}

	/* Must remove any existing attribute, or else confusion reins */
	ldb_msg_remove_attr(msg, "allowedChildClassesEffective");

	oc_el = ldb_msg_find_element(sd_msg, "objectClass");
	ret = get_sd_from_ldb_message(msg, sd_msg, &sd);
	if (ret != LDB_SUCCESS) {
		return ret;
	}
	ret = get_dom_sid_from_ldb_message(msg, sd_msg, &sid);

	if (ret != LDB_SUCCESS) {
		return ret;
	}
	for (i=0; oc_el && i < oc_el->num_values; i++) {
		sclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &oc_el->values[i]);
		if (!sclass) {
			/* We don't know this class?  what is going on? */
			continue;
		}

		for (j=0; sclass->possibleInferiors && sclass->possibleInferiors[j]; j++) {
			ret = acl_check_access_on_class(module,
							msg,
							sd,
							sid,
							SEC_ADS_CREATE_CHILD,
							sclass->possibleInferiors[j]);
			if (ret == LDB_SUCCESS) {
				ldb_msg_add_string(msg, "allowedChildClassesEffective",
						   sclass->possibleInferiors[j]);
			}
		}
	}
	allowedClasses = ldb_msg_find_element(msg, "allowedChildClassesEffective");
	if (!allowedClasses) {
		return LDB_SUCCESS;
	}

	if (allowedClasses->num_values > 1) {
		qsort(allowedClasses->values,
		      allowedClasses->num_values,
		      sizeof(*allowedClasses->values),
		      (comparison_fn_t)data_blob_cmp);
		for (i=1 ; i < allowedClasses->num_values; i++) {
			struct ldb_val *val1 = &allowedClasses->values[i-1];
			struct ldb_val *val2 = &allowedClasses->values[i];
			if (data_blob_cmp(val1, val2) == 0) {
				memmove(val1, val2, (allowedClasses->num_values - i) * sizeof( struct ldb_val));
				allowedClasses->num_values--;
				i--;
			}
		}
	}
	return LDB_SUCCESS;
}
Пример #6
0
static int acl_allowedAttributes(struct ldb_module *module,
				 struct ldb_message *sd_msg,
				 struct ldb_message *msg,
				 struct acl_context *ac)
{
	struct ldb_message_element *oc_el;
	struct ldb_context *ldb = ldb_module_get_ctx(module);
	const struct dsdb_schema *schema = dsdb_get_schema(ldb);
	TALLOC_CTX *mem_ctx;
	const char **attr_list;
	int i, ret;

	/* If we don't have a schema yet, we can't do anything... */
	if (schema == NULL) {
		return LDB_SUCCESS;
	}

	/* Must remove any existing attribute */
	if (ac->allowedAttributes) {
		ldb_msg_remove_attr(msg, "allowedAttributes");
	}

	mem_ctx = talloc_new(msg);
	if (!mem_ctx) {
		ldb_oom(ldb);
		return LDB_ERR_OPERATIONS_ERROR;
	}

	oc_el = ldb_msg_find_element(sd_msg, "objectClass");
	attr_list = dsdb_full_attribute_list(mem_ctx, schema, oc_el, DSDB_SCHEMA_ALL);
	if (!attr_list) {
		ldb_asprintf_errstring(ldb, "acl: Failed to get list of attributes");
		talloc_free(mem_ctx);
		return LDB_ERR_OPERATIONS_ERROR;
	}
	if (ac->allowedAttributes) {
		for (i=0; attr_list && attr_list[i]; i++) {
			ldb_msg_add_string(msg, "allowedAttributes", attr_list[i]);
		}
	}
	if (ac->allowedAttributesEffective) {
		struct security_descriptor *sd;
		struct dom_sid *sid = NULL;
		ldb_msg_remove_attr(msg, "allowedAttributesEffective");
		if (ac->user_type == SECURITY_SYSTEM) {
			for (i=0; attr_list && attr_list[i]; i++) {
				ldb_msg_add_string(msg, "allowedAttributesEffective", attr_list[i]);
			}
			return LDB_SUCCESS;
		}

		ret = get_sd_from_ldb_message(mem_ctx, sd_msg, &sd);

		if (ret != LDB_SUCCESS) {
			return ret;
		}
		ret = get_dom_sid_from_ldb_message(mem_ctx, sd_msg, &sid);

		if (ret != LDB_SUCCESS) {
			return ret;
		}
		for (i=0; attr_list && attr_list[i]; i++) {
			struct dsdb_attribute *attr = dsdb_attribute_by_lDAPDisplayName(schema,
											attr_list[i]);
			if (!attr) {
				return LDB_ERR_OPERATIONS_ERROR;
			}
			/* remove constructed attributes */
			if (attr->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED
			    || attr->systemOnly
			    || (attr->linkID != 0 && attr->linkID % 2 != 0 )) {
				continue;
			}
			ret = acl_check_access_on_attribute(module,
							    msg,
							    sd,
							    sid,
							    SEC_ADS_WRITE_PROP,
							    attr);
			if (ret == LDB_SUCCESS) {
				ldb_msg_add_string(msg, "allowedAttributesEffective", attr_list[i]);
			}
		}
	}
	return LDB_SUCCESS;
}
Пример #7
0
static int acl_search_callback(struct ldb_request *req, struct ldb_reply *ares)
{
	struct ldb_context *ldb;
	struct acl_context *ac;
	struct acl_private *data;
	struct ldb_result *acl_res;
	static const char *acl_attrs[] = {
		"objectClass",
		"nTSecurityDescriptor",
		"objectSid",
		NULL
	};
	int ret, i;

	ac = talloc_get_type(req->context, struct acl_context);
	data = talloc_get_type(ldb_module_get_private(ac->module), struct acl_private);
	ldb = ldb_module_get_ctx(ac->module);

	if (!ares) {
		return ldb_module_done(ac->req, NULL, NULL,
				       LDB_ERR_OPERATIONS_ERROR);
	}
	if (ares->error != LDB_SUCCESS) {
		return ldb_module_done(ac->req, ares->controls,
				       ares->response, ares->error);
	}

	switch (ares->type) {
	case LDB_REPLY_ENTRY:
		if (ac->allowedAttributes 
		    || ac->allowedChildClasses
		    || ac->allowedChildClassesEffective
		    || ac->allowedAttributesEffective
		    || ac->sDRightsEffective) {
			ret = ldb_search(ldb, ac, &acl_res, ares->message->dn, LDB_SCOPE_BASE, acl_attrs, NULL);
			if (ret != LDB_SUCCESS) {
				return ldb_module_done(ac->req, NULL, NULL, ret);
			}
			if (ac->allowedAttributes || ac->allowedAttributesEffective) {
				ret = acl_allowedAttributes(ac->module, acl_res->msgs[0], ares->message, ac);
				if (ret != LDB_SUCCESS) {
					return ldb_module_done(ac->req, NULL, NULL, ret);
				}
			}
			if (ac->allowedChildClasses) {
				ret = acl_childClasses(ac->module, acl_res->msgs[0],
						       ares->message, "allowedChildClasses");
				if (ret != LDB_SUCCESS) {
					return ldb_module_done(ac->req, NULL, NULL, ret);
				}
			}
			if (ac->allowedChildClassesEffective) {
				ret = acl_childClassesEffective(ac->module,
								acl_res->msgs[0], ares->message, ac);
				if (ret != LDB_SUCCESS) {
					return ldb_module_done(ac->req, NULL, NULL, ret);
				}
			}
			if (ac->sDRightsEffective) {
				ret = acl_sDRightsEffective(ac->module,
							    acl_res->msgs[0], ares->message, ac);
				if (ret != LDB_SUCCESS) {
					return ldb_module_done(ac->req, NULL, NULL, ret);
				}
			}
		}
		if (data && data->password_attrs) {
			if (ac->user_type != SECURITY_SYSTEM) {
				for (i = 0; data->password_attrs[i]; i++) {
					ldb_msg_remove_attr(ares->message, data->password_attrs[i]);
				}
			}
		}
		return ldb_module_send_entry(ac->req, ares->message, ares->controls);

	case LDB_REPLY_REFERRAL:
		return ldb_module_send_referral(ac->req, ares->referral);

	case LDB_REPLY_DONE:
		return ldb_module_done(ac->req, ares->controls,
				       ares->response, LDB_SUCCESS);

	}
	return LDB_SUCCESS;
}
Пример #8
0
static int dsdb_schema_set_attributes(struct ldb_context *ldb, struct dsdb_schema *schema, bool write_attributes)
{
	int ret = LDB_SUCCESS;
	struct ldb_result *res;
	struct ldb_result *res_idx;
	struct dsdb_attribute *attr;
	struct ldb_message *mod_msg;
	TALLOC_CTX *mem_ctx;
	struct ldb_message *msg;
	struct ldb_message *msg_idx;

	/* setup our own attribute name to schema handler */
	ldb_schema_attribute_set_override_handler(ldb, dsdb_attribute_handler_override, schema);

	if (!write_attributes) {
		return ret;
	}

	mem_ctx = talloc_new(ldb);
	if (!mem_ctx) {
		return ldb_oom(ldb);
	}

	msg = ldb_msg_new(mem_ctx);
	if (!msg) {
		ldb_oom(ldb);
		goto op_error;
	}
	msg_idx = ldb_msg_new(mem_ctx);
	if (!msg_idx) {
		ldb_oom(ldb);
		goto op_error;
	}
	msg->dn = ldb_dn_new(msg, ldb, "@ATTRIBUTES");
	if (!msg->dn) {
		ldb_oom(ldb);
		goto op_error;
	}
	msg_idx->dn = ldb_dn_new(msg_idx, ldb, "@INDEXLIST");
	if (!msg_idx->dn) {
		ldb_oom(ldb);
		goto op_error;
	}

	ret = ldb_msg_add_string(msg_idx, "@IDXONE", "1");
	if (ret != LDB_SUCCESS) {
		goto op_error;
	}


	ret = ldb_msg_add_string(msg_idx, "@IDXVERSION", SAMDB_INDEXING_VERSION);
	if (ret != LDB_SUCCESS) {
		goto op_error;
	}

	for (attr = schema->attributes; attr; attr = attr->next) {
		const char *syntax = attr->syntax->ldb_syntax;

		if (!syntax) {
			syntax = attr->syntax->ldap_oid;
		}

		/*
		 * Write out a rough approximation of the schema
		 * as an @ATTRIBUTES value, for bootstrapping
		 */
		if (strcmp(syntax, LDB_SYNTAX_INTEGER) == 0) {
			ret = ldb_msg_add_string(msg, attr->lDAPDisplayName, "INTEGER");
		} else if (strcmp(syntax, LDB_SYNTAX_DIRECTORY_STRING) == 0) {
			ret = ldb_msg_add_string(msg, attr->lDAPDisplayName, "CASE_INSENSITIVE");
		}
		if (ret != LDB_SUCCESS) {
			break;
		}

		if (attr->searchFlags & SEARCH_FLAG_ATTINDEX) {
			ret = ldb_msg_add_string(msg_idx, "@IDXATTR", attr->lDAPDisplayName);
			if (ret != LDB_SUCCESS) {
				break;
			}
		}
	}

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

	/*
	 * Try to avoid churning the attributes too much,
	 * we only want to do this if they have changed
	 */
	ret = ldb_search(ldb, mem_ctx, &res, msg->dn, LDB_SCOPE_BASE, NULL,
			 NULL);
	if (ret == LDB_ERR_NO_SUCH_OBJECT) {
		ret = ldb_add(ldb, msg);
	} else if (ret != LDB_SUCCESS) {
	} else if (res->count != 1) {
		ret = ldb_add(ldb, msg);
	} else {
		ret = LDB_SUCCESS;
		/* Annoyingly added to our search results */
		ldb_msg_remove_attr(res->msgs[0], "distinguishedName");

		ret = ldb_msg_difference(ldb, mem_ctx,
		                         res->msgs[0], msg, &mod_msg);
		if (ret != LDB_SUCCESS) {
			goto op_error;
		}
		if (mod_msg->num_elements > 0) {
			ret = dsdb_replace(ldb, mod_msg, 0);
		}
		talloc_free(mod_msg);
	}

	if (ret == LDB_ERR_OPERATIONS_ERROR || ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS || ret == LDB_ERR_INVALID_DN_SYNTAX) {
		/* We might be on a read-only DB or LDAP */
		ret = LDB_SUCCESS;
	}
	if (ret != LDB_SUCCESS) {
		talloc_free(mem_ctx);
		return ret;
	}

	/* Now write out the indexes, as found in the schema (if they have changed) */

	ret = ldb_search(ldb, mem_ctx, &res_idx, msg_idx->dn, LDB_SCOPE_BASE,
			 NULL, NULL);
	if (ret == LDB_ERR_NO_SUCH_OBJECT) {
		ret = ldb_add(ldb, msg_idx);
	} else if (ret != LDB_SUCCESS) {
	} else if (res_idx->count != 1) {
		ret = ldb_add(ldb, msg_idx);
	} else {
		ret = LDB_SUCCESS;
		/* Annoyingly added to our search results */
		ldb_msg_remove_attr(res_idx->msgs[0], "distinguishedName");

		ret = ldb_msg_difference(ldb, mem_ctx,
		                         res_idx->msgs[0], msg_idx, &mod_msg);
		if (ret != LDB_SUCCESS) {
			goto op_error;
		}
		if (mod_msg->num_elements > 0) {
			ret = dsdb_replace(ldb, mod_msg, 0);
		}
		talloc_free(mod_msg);
	}
	if (ret == LDB_ERR_OPERATIONS_ERROR || ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS || ret == LDB_ERR_INVALID_DN_SYNTAX) {
		/* We might be on a read-only DB */
		ret = LDB_SUCCESS;
	}
	talloc_free(mem_ctx);
	return ret;

op_error:
	talloc_free(mem_ctx);
	return ldb_operr(ldb);
}
Пример #9
0
/*
  post process a search result record. For any search_sub[] attributes that were
  asked for, we need to call the appropriate copy routine to copy the result
  into the message, then remove any attributes that we added to the search but
  were not asked for by the user
*/
static int operational_search_post_process(struct ldb_module *module,
					   struct ldb_message *msg,
					   enum ldb_scope scope,
					   const char * const *attrs_from_user,
					   const char * const *attrs_searched_for,
					   struct op_controls_flags* controls_flags,
					   struct op_attributes_operations *list,
					   unsigned int list_size,
					   struct op_attributes_replace *list_replace,
					   unsigned int list_replace_size,
					   struct ldb_request *parent)
{
	struct ldb_context *ldb;
	unsigned int i, a = 0;
	bool constructed_attributes = false;

	ldb = ldb_module_get_ctx(module);

	/* removed any attrs that should not be shown to the user */
	for (i=0; i < list_size; i++) {
		ldb_msg_remove_attr(msg, list[i].attr);
	}

	for (a=0; a < list_replace_size; a++) {
		if (check_keep_control_for_attribute(controls_flags,
						     list_replace[a].attr)) {
			continue;
		}

		/* construct the new attribute, using either a supplied
			constructor or a simple copy */
		constructed_attributes = true;
		if (list_replace[a].constructor != NULL) {
			if (list_replace[a].constructor(module, msg, scope, parent) != LDB_SUCCESS) {
				goto failed;
			}
		} else if (ldb_msg_copy_attr(msg,
					     list_replace[a].replace,
					     list_replace[a].attr) != LDB_SUCCESS) {
			goto failed;
		}
	}

	/* Deletion of the search helper attributes are needed if:
	 * - we generated constructed attributes and
	 * - we aren't requesting all attributes
	 */
	if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
		for (i=0; i < list_replace_size; i++) {
			/* remove the added search helper attributes, unless
			 * they were asked for by the user */
			if (list_replace[i].replace != NULL &&
			    !ldb_attr_in_list(attrs_from_user, list_replace[i].replace)) {
				ldb_msg_remove_attr(msg, list_replace[i].replace);
			}
			if (list_replace[i].extra_attr != NULL &&
			    !ldb_attr_in_list(attrs_from_user, list_replace[i].extra_attr)) {
				ldb_msg_remove_attr(msg, list_replace[i].extra_attr);
			}
		}
	}

	return 0;

failed:
	ldb_debug_set(ldb, LDB_DEBUG_WARNING,
		      "operational_search_post_process failed for attribute '%s' - %s",
		      attrs_from_user[a], ldb_errstring(ldb));
	return -1;
}
Пример #10
0
/*
  post process a search result record. For any search_sub[] attributes that were
  asked for, we need to call the appropriate copy routine to copy the result
  into the message, then remove any attributes that we added to the search but
  were not asked for by the user
*/
static int operational_search_post_process(struct ldb_module *module,
					   struct ldb_message *msg,
					   enum ldb_scope scope,
					   const char * const *attrs_from_user,
					   const char * const *attrs_searched_for,
					   struct op_controls_flags* controls_flags,
					   struct ldb_request *parent)
{
	struct ldb_context *ldb;
	unsigned int i, a = 0;
	bool constructed_attributes = false;

	ldb = ldb_module_get_ctx(module);

	/* removed any attrs that should not be shown to the user */
	for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
		switch (operational_remove[i].op) {
		case OPERATIONAL_REMOVE_UNASKED:
			if (ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
				continue;
			}
			if (ldb_attr_in_list(attrs_searched_for, operational_remove[i].attr)) {
				continue;
			}
		case OPERATIONAL_REMOVE_ALWAYS:
			ldb_msg_remove_attr(msg, operational_remove[i].attr);
			break;
		case OPERATIONAL_REMOVE_UNLESS_CONTROL:
			if (!check_keep_control_for_attribute(controls_flags, operational_remove[i].attr)) {
				ldb_msg_remove_attr(msg, operational_remove[i].attr);
				break;
			} else {
				continue;
			}
		case OPERATIONAL_SD_FLAGS:
			if (controls_flags->sd ||
			    ldb_attr_in_list(attrs_from_user, operational_remove[i].attr)) {
				continue;
			}
			ldb_msg_remove_attr(msg, operational_remove[i].attr);
			break;
		}
	}

	for (a=0;attrs_from_user && attrs_from_user[a];a++) {
		if (check_keep_control_for_attribute(controls_flags, attrs_from_user[a])) {
			continue;
		}
		for (i=0;i<ARRAY_SIZE(search_sub);i++) {
			if (ldb_attr_cmp(attrs_from_user[a], search_sub[i].attr) != 0) {
				continue;
			}

			/* construct the new attribute, using either a supplied
			   constructor or a simple copy */
			constructed_attributes = true;
			if (search_sub[i].constructor != NULL) {
				if (search_sub[i].constructor(module, msg, scope, parent) != LDB_SUCCESS) {
					goto failed;
				}
			} else if (ldb_msg_copy_attr(msg,
						     search_sub[i].replace,
						     search_sub[i].attr) != LDB_SUCCESS) {
				goto failed;
			}
		}
	}

	/* Deletion of the search helper attributes are needed if:
	 * - we generated constructed attributes and
	 * - we aren't requesting all attributes
	 */
	if ((constructed_attributes) && (!ldb_attr_in_list(attrs_from_user, "*"))) {
		for (i=0;i<ARRAY_SIZE(search_sub);i++) {
			/* remove the added search helper attributes, unless
			 * they were asked for by the user */
			if (search_sub[i].replace != NULL && 
			    !ldb_attr_in_list(attrs_from_user, search_sub[i].replace)) {
				ldb_msg_remove_attr(msg, search_sub[i].replace);
			}
			if (search_sub[i].extra_attr != NULL && 
			    !ldb_attr_in_list(attrs_from_user, search_sub[i].extra_attr)) {
				ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
			}
		}
	}

	return 0;

failed:
	ldb_debug_set(ldb, LDB_DEBUG_WARNING,
		      "operational_search_post_process failed for attribute '%s' - %s",
		      attrs_from_user[a], ldb_errstring(ldb));
	return -1;
}