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 "includes.h"
-#include "ldb/include/ldb.h"
-#include "ldb/include/ldb_errors.h"
-#include "ldb/include/ldb_private.h"
+#include "ldb_module.h"
#include "auth/auth.h"
#include "libcli/security/security.h"
+#include "dsdb/samdb/samdb.h"
+#include "param/param.h"
+#include "lib/util/tsort.h"
/* Kludge ACL rules:
*
*
*/
-enum user_is {
- ANONYMOUS,
- USER,
- ADMINISTRATOR,
- SYSTEM
-};
-
struct kludge_private_data {
const char **password_attrs;
+ bool acl_perform;
};
-static enum user_is what_is_user(struct ldb_module *module)
+static enum security_user_level what_is_user(struct ldb_module *module)
{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
struct auth_session_info *session_info
- = ldb_get_opaque(module->ldb, "sessionInfo");
- if (!session_info) {
- return ANONYMOUS;
- }
-
- if (security_token_is_system(session_info->security_token)) {
- return SYSTEM;
- }
-
- if (security_token_is_anonymous(session_info->security_token)) {
- return ANONYMOUS;
- }
-
- if (security_token_has_builtin_administrators(session_info->security_token)) {
- return ADMINISTRATOR;
- }
-
- if (security_token_has_nt_authenticated_users(session_info->security_token)) {
- return USER;
- }
-
- return ANONYMOUS;
+ = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
+ return security_session_user_level(session_info, NULL);
}
static const char *user_name(TALLOC_CTX *mem_ctx, struct ldb_module *module)
{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
struct auth_session_info *session_info
- = ldb_get_opaque(module->ldb, "sessionInfo");
+ = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
if (!session_info) {
return "UNKNOWN (NULL)";
}
struct kludge_acl_context {
struct ldb_module *module;
- void *up_context;
- int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
-
- enum user_is user_type;
+ struct ldb_request *req;
+
+ enum security_user_level user_type;
+ bool allowedAttributes;
+ bool allowedAttributesEffective;
+ bool allowedChildClasses;
+ bool allowedChildClassesEffective;
+ const char * const *attrs;
};
-static int kludge_acl_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+/* 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;
+ /* We need to ensure that the strings returned are valid for as long as the msg is valid */
+ const struct dsdb_schema *schema = dsdb_get_schema(ldb, msg);
+ TALLOC_CTX *mem_ctx;
+ const char **attr_list;
+ unsigned int i;
+ int 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, &allowedAttributes);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ mem_ctx = talloc_new(msg);
+ if (!mem_ctx) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* 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");
+
+ attr_list = dsdb_full_attribute_list(mem_ctx, schema, oc_el, 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;
+
+}
+/* read all objectClasses */
+
+static int kludge_acl_childClasses(struct ldb_context *ldb, struct ldb_message *msg,
+ const char *attrName)
+{
+ struct ldb_message_element *oc_el;
+ struct ldb_message_element *allowedClasses;
+
+ /* We need to ensure that the strings returned are valid for as long as the msg is valid */
+ const struct dsdb_schema *schema = dsdb_get_schema(ldb, msg);
+ const struct dsdb_class *sclass;
+ unsigned int i, j;
+ int 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;
+ }
+
+ /* 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++) {
+ 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) {
+ TYPESAFE_QSORT(allowedClasses->values, allowedClasses->num_values, 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;
+}
+
+/* find all attributes allowed by all these objectClasses */
+
+static int kludge_acl_callback(struct ldb_request *req, struct ldb_reply *ares)
{
+ struct ldb_context *ldb;
struct kludge_acl_context *ac;
struct kludge_private_data *data;
- int i;
+ unsigned int i;
+ int ret;
- if (!context || !ares) {
- ldb_set_errstring(ldb, "NULL Context or Result in callback");
- goto error;
+ ac = talloc_get_type(req->context, struct kludge_acl_context);
+ data = talloc_get_type(ldb_module_get_private(ac->module), struct kludge_private_data);
+ 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);
}
- ac = talloc_get_type(context, struct kludge_acl_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(ldb,
+ ares->message,
+ "allowedAttributes");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+ if (ac->allowedChildClasses) {
+ ret = kludge_acl_childClasses(ldb,
+ ares->message,
+ "allowedChildClasses");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
- if (ares->type == LDB_REPLY_ENTRY
- && data && data->password_attrs) /* if we are not initialized just get through */
- {
- switch (ac->user_type) {
- case SYSTEM:
- 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(ldb, ares->message,
+ "allowedAttributesEffective");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+ if (ac->allowedChildClassesEffective) {
+ ret = kludge_acl_childClasses(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(ldb, ares->message,
+ "allowedAttributesEffective");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ }
+ if (ac->allowedChildClassesEffective) {
+ ret = kludge_acl_childClasses(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, ares->controls);
+
+ case LDB_REPLY_REFERRAL:
+ return ldb_module_send_referral(ac->req, ares->referral);
-error:
- talloc_free(ares);
- return LDB_ERR_OPERATIONS_ERROR;
+ case LDB_REPLY_DONE:
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, LDB_SUCCESS);
+
+ }
+ return LDB_SUCCESS;
}
static int kludge_acl_search(struct ldb_module *module, struct ldb_request *req)
{
+ struct ldb_context *ldb;
struct kludge_acl_context *ac;
struct ldb_request *down_req;
struct kludge_private_data *data;
- int ret, i;
+ const char * const *attrs;
+ int ret;
+ unsigned int i;
- req->handle = NULL;
+ ldb = ldb_module_get_ctx(module);
ac = talloc(req, struct kludge_acl_context);
if (ac == NULL) {
+ ldb_oom(ldb);
return LDB_ERR_OPERATIONS_ERROR;
}
- data = talloc_get_type(module->private_data, struct kludge_private_data);
+ data = talloc_get_type(ldb_module_get_private(module), struct kludge_private_data);
+
+ if (data && data->acl_perform)
+ return ldb_next_request(module, req);
ac->module = module;
- ac->up_context = req->context;
- ac->up_callback = req->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");
- 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;
-
+ 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;
+ }
- /* FIXME: I hink we should copy the tree and keep the original
- * unmodified. SSS */
/* replace any attributes in the parse tree that are private,
- so we don't allow a search for 'sambaPassword=penguin',
+ 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 SYSTEM:
+ case SECURITY_SYSTEM:
break;
default:
+ /* FIXME: We should copy the tree and keep the original unmodified. */
/* remove password attributes */
- for (i = 0; data && data->password_attrs && data->password_attrs[i]; i++) {
- ldb_parse_tree_attr_replace(down_req->op.search.tree,
+
+ 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");
}
}
- down_req->controls = req->controls;
-
- down_req->context = ac;
- down_req->callback = kludge_acl_callback;
- ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
-
- /* perform the search */
- ret = ldb_next_request(module, down_req);
-
- /* 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->handle = down_req->handle;
+ ret = ldb_build_search_req_ex(&down_req,
+ 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 ret;
}
- 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);
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ enum security_user_level user_type = what_is_user(module);
+ struct kludge_private_data *data = talloc_get_type(ldb_module_get_private(module),
+ struct kludge_private_data);
+
+ if (data->acl_perform)
+ return ldb_next_request(module, req);
+
switch (user_type) {
- case SYSTEM:
- case ADMINISTRATOR:
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
return ldb_next_request(module, req);
default:
- ldb_asprintf_errstring(module->ldb,
+ ldb_asprintf_errstring(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;
+ }
+}
+
+static int kludge_acl_extended(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ enum security_user_level user_type;
+
+ /* allow everybody to read the sequence number */
+ if (strcmp(req->op.extended.oid,
+ LDB_EXTENDED_SEQUENCE_NUMBER) == 0) {
+ return ldb_next_request(module, req);
+ }
+
+ user_type = what_is_user(module);
+
+ switch (user_type) {
+ case SECURITY_SYSTEM:
+ case SECURITY_ADMINISTRATOR:
+ return ldb_next_request(module, req);
+ default:
+ ldb_asprintf_errstring(ldb,
"kludge_acl_change: "
"attempted database modify not permitted. "
"User %s is not SYSTEM or an administrator",
static int kludge_acl_init(struct ldb_module *module)
{
- int ret, i;
+ struct ldb_context *ldb;
+ int ret;
+ unsigned int i;
TALLOC_CTX *mem_ctx = talloc_new(module);
static const char *attrs[] = { "passwordAttribute", NULL };
struct ldb_result *res;
struct kludge_private_data *data;
+ ldb = ldb_module_get_ctx(module);
+
data = talloc(module, struct kludge_private_data);
if (data == NULL) {
+ ldb_oom(ldb);
return LDB_ERR_OPERATIONS_ERROR;
}
data->password_attrs = NULL;
- module->private_data = data;
+ data->acl_perform = lp_parm_bool(ldb_get_opaque(ldb, "loadparm"),
+ NULL, "acl", "perform", false);
+ ldb_module_set_private(module, data);
if (!mem_ctx) {
+ ldb_oom(ldb);
return LDB_ERR_OPERATIONS_ERROR;
}
- ret = ldb_search(module->ldb, ldb_dn_new(mem_ctx, module->ldb, "@KLUDGEACL"),
- LDB_SCOPE_BASE,
- NULL, attrs,
- &res);
+ ret = ldb_search(ldb, mem_ctx, &res,
+ ldb_dn_new(mem_ctx, 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(ldb);
return LDB_ERR_OPERATIONS_ERROR;
}
for (i=0; i < password_attributes->num_values; i++) {
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,
+/* .search = kludge_acl_search,
.add = kludge_acl_change,
.modify = kludge_acl_change,
.del = kludge_acl_change,
- .rename = kludge_acl_change,
- .extended = kludge_acl_change,
+ .rename = kludge_acl_change, */
+ .extended = kludge_acl_extended,
.init_context = kludge_acl_init
};
-
-int ldb_kludge_acl_init(void)
-{
- return ldb_register_module(&kludge_acl_ops);
-}