ldb database library
Copyright (C) Andrew Bartlett 2005
- Copyright (C) Simo Sorce 2006
+ Copyright (C) Simo Sorce 2006-2008
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
#include "ldb/include/ldb_private.h"
#include "auth/auth.h"
#include "libcli/security/security.h"
+#include "dsdb/samdb/samdb.h"
/* Kludge ACL rules:
*
*
*/
-enum user_is {
- ANONYMOUS,
- USER,
- ADMINISTRATOR,
- SYSTEM
-};
-
struct kludge_private_data {
const char **password_attrs;
};
-static enum user_is what_is_user(struct ldb_module *module)
+static enum security_user_level what_is_user(struct ldb_module *module)
+{
+ struct auth_session_info *session_info
+ = (struct auth_session_info *)ldb_get_opaque(module->ldb, "sessionInfo");
+ return security_session_user_level(session_info);
+}
+
+static const char *user_name(TALLOC_CTX *mem_ctx, struct ldb_module *module)
{
struct auth_session_info *session_info
- = ldb_get_opaque(module->ldb, "sessionInfo");
+ = (struct auth_session_info *)ldb_get_opaque(module->ldb, "sessionInfo");
if (!session_info) {
- return ANONYMOUS;
+ return "UNKNOWN (NULL)";
}
- if (security_token_is_system(session_info->security_token)) {
- return SYSTEM;
+ return talloc_asprintf(mem_ctx, "%s\\%s",
+ session_info->server_info->domain_name,
+ session_info->server_info->account_name);
+}
+
+/* search */
+struct kludge_acl_context {
+
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ enum security_user_level user_type;
+ bool allowedAttributes;
+ bool allowedAttributesEffective;
+ bool allowedChildClasses;
+ bool allowedChildClassesEffective;
+ const char * const *attrs;
+};
+
+/* read all objectClasses */
+
+static int kludge_acl_allowedAttributes(struct ldb_context *ldb, struct ldb_message *msg,
+ const char *attrName)
+{
+ struct ldb_message_element *oc_el;
+ struct ldb_message_element *allowedAttributes;
+ const struct dsdb_schema *schema = dsdb_get_schema(ldb);
+ TALLOC_CTX *mem_ctx;
+ char **objectclass_list, **attr_list;
+ int i, ret;
+
+ /* If we don't have a schema yet, we can't do anything... */
+ if (schema == NULL) {
+ return LDB_SUCCESS;
}
- if (security_token_is_anonymous(session_info->security_token)) {
- return ANONYMOUS;
+ /* Must remove any existing attribute, or else confusion reins */
+ ldb_msg_remove_attr(msg, attrName);
+ ret = ldb_msg_add_empty(msg, attrName, 0, &allowedAttributes);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ mem_ctx = talloc_new(msg);
+ if (!mem_ctx) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
}
- if (security_token_has_builtin_administrators(session_info->security_token)) {
- return ADMINISTRATOR;
+ /* To ensure that oc_el is valid, we must look for it after
+ we alter the element array in ldb_msg_add_empty() */
+ oc_el = ldb_msg_find_element(msg, "objectClass");
+
+ objectclass_list = talloc_array(mem_ctx, char *, oc_el->num_values + 1);
+ if (!objectclass_list) {
+ ldb_oom(ldb);
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
}
- if (security_token_has_nt_authenticated_users(session_info->security_token)) {
- return USER;
+ for (i=0; oc_el && i < oc_el->num_values; i++) {
+ objectclass_list[i] = (char *)oc_el->values[i].data;
}
+ objectclass_list[i] = NULL;
+
+ attr_list = dsdb_full_attribute_list(mem_ctx, schema, (const char **)objectclass_list, DSDB_SCHEMA_ALL);
+ if (!attr_list) {
+ ldb_asprintf_errstring(ldb, "kludge_acl: Failed to get list of attributes create %s attribute", attrName);
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i=0; attr_list && attr_list[i]; i++) {
+ ldb_msg_add_string(msg, attrName, attr_list[i]);
+ }
+ talloc_free(mem_ctx);
+ return LDB_SUCCESS;
- return ANONYMOUS;
}
+/* read all objectClasses */
-static const char *user_name(TALLOC_CTX *mem_ctx, struct ldb_module *module)
+static int kludge_acl_childClasses(struct ldb_context *ldb, struct ldb_message *msg,
+ const char *attrName)
{
- struct auth_session_info *session_info
- = ldb_get_opaque(module->ldb, "sessionInfo");
- if (!session_info) {
- return "UNKNOWN (NULL)";
+ struct ldb_message_element *oc_el;
+ struct ldb_message_element *allowedClasses;
+ const struct dsdb_schema *schema = dsdb_get_schema(ldb);
+ const struct dsdb_class *class;
+ 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;
}
- return talloc_asprintf(mem_ctx, "%s\\%s",
- session_info->server_info->domain_name,
- session_info->server_info->account_name);
-}
+ /* To ensure that oc_el is valid, we must look for it after
+ we alter the element array in ldb_msg_add_empty() */
+ oc_el = ldb_msg_find_element(msg, "objectClass");
+
+ for (i=0; oc_el && i < oc_el->num_values; i++) {
+ class = dsdb_class_by_lDAPDisplayName(schema, (const char *)oc_el->values[i].data);
+ if (!class) {
+ /* We don't know this class? what is going on? */
+ continue;
+ }
-/* search */
-struct kludge_acl_async_context {
+ for (j=0; class->possibleInferiors && class->possibleInferiors[j]; j++) {
+ ldb_msg_add_string(msg, attrName, class->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--;
+ }
+ }
+ }
- struct ldb_module *module;
- void *up_context;
- int (*up_callback)(struct ldb_context *, void *, struct ldb_async_result *);
+ return LDB_SUCCESS;
- enum user_is user_type;
-};
+}
-static int kludge_acl_async_callback(struct ldb_context *ldb, void *context, struct ldb_async_result *ares)
+/* find all attributes allowed by all these objectClasses */
+
+static int kludge_acl_callback(struct ldb_request *req, struct ldb_reply *ares)
{
- struct kludge_acl_async_context *ac;
+ struct kludge_acl_context *ac;
struct kludge_private_data *data;
- int i;
+ int i, ret;
+
+ ac = talloc_get_type(req->context, struct kludge_acl_context);
+ data = talloc_get_type(ac->module->private_data, struct kludge_private_data);
- if (!context || !ares) {
- ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
- goto error;
+ 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);
}
- ac = talloc_get_type(context, struct kludge_acl_async_context);
- data = talloc_get_type(ac->module->private_data, struct kludge_private_data);
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ if (ac->allowedAttributes) {
+ ret = kludge_acl_allowedAttributes(ac->module->ldb,
+ ares->message,
+ "allowedAttributes");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+ if (ac->allowedChildClasses) {
+ ret = kludge_acl_childClasses(ac->module->ldb,
+ ares->message,
+ "allowedChildClasses");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
- if (ares->type == LDB_REPLY_ENTRY
- && data->password_attrs) /* if we are not initialized just get through */
- {
- switch (ac->user_type) {
- case SYSTEM:
- case ADMINISTRATOR:
- break;
- default:
- /* remove password attributes */
- for (i = 0; data->password_attrs[i]; i++) {
- ldb_msg_remove_attr(ares->message, data->password_attrs[i]);
+ if (data && data->password_attrs) /* if we are not initialized just get through */
+ {
+ switch (ac->user_type) {
+ case SECURITY_SYSTEM:
+ if (ac->allowedAttributesEffective) {
+ ret = kludge_acl_allowedAttributes(ac->module->ldb, ares->message,
+ "allowedAttributesEffective");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+ if (ac->allowedChildClassesEffective) {
+ ret = kludge_acl_childClasses(ac->module->ldb, ares->message,
+ "allowedChildClassesEffective");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+ break;
+
+ case SECURITY_ADMINISTRATOR:
+ if (ac->allowedAttributesEffective) {
+ ret = kludge_acl_allowedAttributes(ac->module->ldb, ares->message,
+ "allowedAttributesEffective");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+ if (ac->allowedChildClassesEffective) {
+ ret = kludge_acl_childClasses(ac->module->ldb, ares->message,
+ "allowedChildClassesEffective");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+ /* fall through */
+ default:
+ /* remove password attributes */
+ for (i = 0; data->password_attrs[i]; i++) {
+ ldb_msg_remove_attr(ares->message, data->password_attrs[i]);
+ }
}
}
- }
- return ac->up_callback(ldb, ac->up_context, ares);
+ if (ac->allowedAttributes ||
+ ac->allowedAttributesEffective ||
+ ac->allowedChildClasses ||
+ ac->allowedChildClassesEffective) {
+
+ if (!ldb_attr_in_list(ac->attrs, "objectClass") &&
+ !ldb_attr_in_list(ac->attrs, "*")) {
+
+ ldb_msg_remove_attr(ares->message,
+ "objectClass");
+ }
+ }
+
+ return ldb_module_send_entry(ac->req, ares->message);
+
+ 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);
-error:
- talloc_free(ares);
- return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return LDB_SUCCESS;
}
-static int kludge_acl_search_async(struct ldb_module *module, struct ldb_request *req)
+static int kludge_acl_search(struct ldb_module *module, struct ldb_request *req)
{
- struct kludge_acl_async_context *ac;
+ struct kludge_acl_context *ac;
struct ldb_request *down_req;
- int ret;
-
- req->async.handle = NULL;
+ struct kludge_private_data *data;
+ const char * const *attrs;
+ int ret, i;
+ struct ldb_control *sd_control;
+ struct ldb_control **sd_saved_controls;
- ac = talloc(req, struct kludge_acl_async_context);
+ ac = talloc(req, struct kludge_acl_context);
if (ac == NULL) {
+ ldb_oom(module->ldb);
return LDB_ERR_OPERATIONS_ERROR;
}
+ data = talloc_get_type(module->private_data, struct kludge_private_data);
+
ac->module = module;
- ac->up_context = req->async.context;
- ac->up_callback = req->async.callback;
+ ac->req = req;
ac->user_type = what_is_user(module);
+ ac->attrs = req->op.search.attrs;
- down_req = talloc_zero(req, struct ldb_request);
- if (down_req == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
+ ac->allowedAttributes = ldb_attr_in_list(req->op.search.attrs, "allowedAttributes");
+
+ ac->allowedAttributesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedAttributesEffective");
+
+ ac->allowedChildClasses = ldb_attr_in_list(req->op.search.attrs, "allowedChildClasses");
+
+ ac->allowedChildClassesEffective = ldb_attr_in_list(req->op.search.attrs, "allowedChildClassesEffective");
+
+ if (ac->allowedAttributes || ac->allowedAttributesEffective || ac->allowedChildClasses || ac->allowedChildClassesEffective) {
+ attrs = ldb_attr_list_copy_add(ac, req->op.search.attrs, "objectClass");
+ } else {
+ attrs = req->op.search.attrs;
}
- down_req->operation = req->operation;
- down_req->op.search.base = req->op.search.base;
- down_req->op.search.scope = req->op.search.scope;
- down_req->op.search.tree = req->op.search.tree;
- down_req->op.search.attrs = req->op.search.attrs;
-
- down_req->controls = req->controls;
+ /* replace any attributes in the parse tree that are private,
+ so we don't allow a search for 'userPassword=penguin',
+ just as we would not allow that attribute to be returned */
+ switch (ac->user_type) {
+ case SECURITY_SYSTEM:
+ break;
+ default:
+ /* FIXME: We should copy the tree and keep the original unmodified. */
+ /* remove password attributes */
- down_req->async.context = ac;
- down_req->async.callback = kludge_acl_async_callback;
- ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
+ if (!data || !data->password_attrs) {
+ break;
+ }
+ for (i = 0; data->password_attrs[i]; i++) {
+ ldb_parse_tree_attr_replace(req->op.search.tree,
+ data->password_attrs[i],
+ "kludgeACLredactedattribute");
+ }
+ }
- /* perform the search */
- ret = ldb_next_request(module, down_req);
+ ret = ldb_build_search_req_ex(&down_req,
+ module->ldb, ac,
+ req->op.search.base,
+ req->op.search.scope,
+ req->op.search.tree,
+ attrs,
+ req->controls,
+ ac, kludge_acl_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
- /* do not free down_req as the call results may be linked to it,
- * it will be freed when the upper level request get freed */
- if (ret == LDB_SUCCESS) {
- req->async.handle = down_req->async.handle;
+ /* check if there's an SD_FLAGS control */
+ sd_control = ldb_request_get_control(down_req, LDB_CONTROL_SD_FLAGS_OID);
+ if (sd_control) {
+ /* save it locally and remove it from the list */
+ /* we do not need to replace them later as we
+ * are keeping the original req intact */
+ if (!save_controls(sd_control, down_req, &sd_saved_controls)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
}
- return ret;
+ /* perform the search */
+ return ldb_next_request(module, down_req);
}
/* ANY change type */
static int kludge_acl_change(struct ldb_module *module, struct ldb_request *req)
{
- enum user_is user_type = what_is_user(module);
+ enum security_user_level user_type = what_is_user(module);
switch (user_type) {
- case SYSTEM:
- case ADMINISTRATOR:
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
return ldb_next_request(module, req);
default:
- ldb_set_errstring(module->ldb,
- talloc_asprintf(req, "kludge_acl_change: "
- "attempted database modify not permitted. User %s is not SYSTEM or an administrator",
- user_name(req, module)));
+ ldb_asprintf_errstring(module->ldb,
+ "kludge_acl_change: "
+ "attempted database modify not permitted. "
+ "User %s is not SYSTEM or an administrator",
+ user_name(req, module));
return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
}
}
-/* start a transaction */
-static int kludge_acl_start_trans(struct ldb_module *module)
-{
- return ldb_next_start_trans(module);
-}
-
-/* end a transaction */
-static int kludge_acl_end_trans(struct ldb_module *module)
-{
- return ldb_next_end_trans(module);
-}
-
-/* delete a transaction */
-static int kludge_acl_del_trans(struct ldb_module *module)
-{
- return ldb_next_del_trans(module);
-}
-
static int kludge_acl_init(struct ldb_module *module)
{
int ret, i;
TALLOC_CTX *mem_ctx = talloc_new(module);
- const char *attrs[] = { "attribute", NULL };
+ static const char *attrs[] = { "passwordAttribute", NULL };
struct ldb_result *res;
struct ldb_message *msg;
struct ldb_message_element *password_attributes;
data = talloc(module, struct kludge_private_data);
if (data == NULL) {
+ ldb_oom(module->ldb);
return LDB_ERR_OPERATIONS_ERROR;
}
module->private_data = data;
if (!mem_ctx) {
+ ldb_oom(module->ldb);
return LDB_ERR_OPERATIONS_ERROR;
}
- ret = ldb_search(module->ldb, ldb_dn_explode(mem_ctx, "@KLUDGEACL"),
- LDB_SCOPE_BASE,
- NULL, attrs,
- &res);
+ ret = ldb_search(module->ldb, mem_ctx, &res,
+ ldb_dn_new(mem_ctx, module->ldb, "@KLUDGEACL"),
+ LDB_SCOPE_BASE, attrs, NULL);
if (ret != LDB_SUCCESS) {
goto done;
}
- talloc_steal(mem_ctx, res);
if (res->count == 0) {
goto done;
}
data->password_attrs = talloc_array(data, const char *, password_attributes->num_values + 1);
if (!data->password_attrs) {
talloc_free(mem_ctx);
+ ldb_oom(module->ldb);
return LDB_ERR_OPERATIONS_ERROR;
}
for (i=0; i < password_attributes->num_values; i++) {
}
data->password_attrs[i] = NULL;
+ ret = ldb_mod_register_control(module, LDB_CONTROL_SD_FLAGS_OID);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR,
+ "partition: Unable to register control with rootdse!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
done:
talloc_free(mem_ctx);
return ldb_next_init(module);
}
-static const struct ldb_module_ops kludge_acl_ops = {
+_PUBLIC_ const struct ldb_module_ops ldb_kludge_acl_module_ops = {
.name = "kludge_acl",
- .search = kludge_acl_search_async,
+ .search = kludge_acl_search,
.add = kludge_acl_change,
.modify = kludge_acl_change,
.del = kludge_acl_change,
.rename = kludge_acl_change,
- .start_transaction = kludge_acl_start_trans,
- .end_transaction = kludge_acl_end_trans,
- .del_transaction = kludge_acl_del_trans,
+ .extended = kludge_acl_change,
.init_context = kludge_acl_init
};
-
-int ldb_kludge_acl_init(void)
-{
- return ldb_register_module(&kludge_acl_ops);
-}