ldb database library
Copyright (C) Andrew Bartlett 2005
+ Copyright (C) Simo Sorce 2006
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_errors.h"
#include "ldb/include/ldb_private.h"
#include "auth/auth.h"
-#include "libcli/security/proto.h"
+#include "libcli/security/security.h"
+#include "dsdb/samdb/samdb.h"
/* Kludge ACL rules:
*
return ANONYMOUS;
}
- if (is_system_token(session_info->security_token)) {
+ if (security_token_is_system(session_info->security_token)) {
return SYSTEM;
}
- if (is_administrator_token(session_info->security_token)) {
+ if (security_token_is_anonymous(session_info->security_token)) {
+ return ANONYMOUS;
+ }
+
+ if (security_token_has_builtin_administrators(session_info->security_token)) {
return ADMINISTRATOR;
}
- if (is_authenticated_token(session_info->security_token)) {
+
+ if (security_token_has_nt_authenticated_users(session_info->security_token)) {
return USER;
}
- if (is_anonymous_token(session_info->security_token)) {
- return ANONYMOUS;
- }
+
return ANONYMOUS;
}
return talloc_asprintf(mem_ctx, "%s\\%s",
session_info->server_info->domain_name,
session_info->server_info->account_name);
- return ANONYMOUS;
}
/* search */
-static int kludge_acl_search(struct ldb_module *module, struct ldb_request *req)
-{
- struct kludge_private_data *data = talloc_get_type(module->private_data, struct kludge_private_data);
- struct ldb_message *msg;
+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;
- int i, j, ret;
+ bool allowedAttributes;
+ bool allowedAttributesEffective;
+ bool allowedChildClasses;
+ bool allowedChildClassesEffective;
+ const char **attrs;
+};
- /* go down the path and wait for reply to filter out stuff if needed */
- ret = ldb_next_request(module, req);
+/* read all objectClasses */
- /* We may not be fully initialised yet, or we might have just
- * got an error */
- if (ret != LDB_SUCCESS || !data->password_attrs) {
+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);
+ const struct dsdb_class *class;
+ int i, j, ret;
+ ret = ldb_msg_add_empty(msg, attrName, 0, &allowedAttributes);
+ 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++) {
+ 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;
+ }
- user_type = what_is_user(module);
- switch (user_type) {
- case SYSTEM:
- case ADMINISTRATOR:
- return ret;
- default:
- /* For every message, remove password attributes */
- for (i=0; i < req->op.search.res->count; i++) {
- msg = req->op.search.res->msgs[i];
- for (j=0; data->password_attrs[j]; j++) {
- ldb_msg_remove_attr(msg, data->password_attrs[j]);
+ for (j=0; class->mayContain && class->mayContain[j]; j++) {
+ ldb_msg_add_string(msg, attrName, class->mayContain[j]);
+ }
+ for (j=0; class->mustContain && class->mustContain[j]; j++) {
+ ldb_msg_add_string(msg, attrName, class->mustContain[j]);
+ }
+ for (j=0; class->systemMayContain && class->systemMayContain[j]; j++) {
+ ldb_msg_add_string(msg, attrName, class->systemMayContain[j]);
+ }
+ for (j=0; class->systemMustContain && class->systemMustContain[j]; j++) {
+ ldb_msg_add_string(msg, attrName, class->systemMustContain[j]);
+ }
+ }
+
+ if (allowedAttributes->num_values > 1) {
+ qsort(allowedAttributes->values,
+ allowedAttributes->num_values,
+ sizeof(*allowedAttributes->values),
+ (comparison_fn_t)data_blob_cmp);
+
+ for (i=1 ; i < allowedAttributes->num_values; i++) {
+ struct ldb_val *val1 = &allowedAttributes->values[i-1];
+ struct ldb_val *val2 = &allowedAttributes->values[i];
+ if (data_blob_cmp(val1, val2) == 0) {
+ memmove(val1, val2, (allowedAttributes->num_values - i) * sizeof( struct ldb_val));
+ allowedAttributes->num_values--;
+ i--;
}
}
}
- return ret;
-}
-/* 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);
- switch (user_type) {
- case SYSTEM:
- case 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)));
- return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
- }
+ return 0;
+
}
+/* read all objectClasses */
-/* start a transaction */
-static int kludge_acl_start_trans(struct ldb_module *module)
+static int kludge_acl_childClasses(struct ldb_context *ldb, struct ldb_message *msg,
+ const char *attrName)
{
- return ldb_next_start_trans(module);
+ 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;
+ 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++) {
+ 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;
+ }
+
+ 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--;
+ }
+ }
+ }
+
+ return 0;
+
}
-/* end a transaction */
-static int kludge_acl_end_trans(struct ldb_module *module)
+/* find all attributes allowed by all these objectClasses */
+
+static int kludge_acl_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
{
- return ldb_next_end_trans(module);
+ struct kludge_acl_context *ac;
+ struct kludge_private_data *data;
+ int i, ret;
+
+ if (!context || !ares) {
+ ldb_set_errstring(ldb, "NULL Context or Result in callback");
+ goto error;
+ }
+
+ ac = talloc_get_type(context, struct kludge_acl_context);
+ data = talloc_get_type(ac->module->private_data, struct kludge_private_data);
+
+ if (ares->type != LDB_REPLY_ENTRY) {
+ return ac->up_callback(ldb, ac->up_context, ares);
+ }
+
+ if (ac->allowedAttributes) {
+ ret = kludge_acl_allowedAttributes(ldb, ares->message, "allowedAttributes");
+ if (ret != LDB_SUCCESS) {
+ return ret;
+
+ }
+ }
+ if (ac->allowedChildClasses) {
+ ret = kludge_acl_childClasses(ldb, ares->message, "allowedChildClasses");
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ if (data && data->password_attrs) /* if we are not initialized just get through */
+ {
+ switch (ac->user_type) {
+ case SYSTEM:
+ case ADMINISTRATOR:
+ if (ac->allowedAttributesEffective) {
+ ret = kludge_acl_allowedAttributes(ldb, ares->message, "allowedAttributesEffective");
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ if (ac->allowedChildClassesEffective) {
+ ret = kludge_acl_childClasses(ldb, ares->message, "allowedChildClassesEffective");
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+ break;
+ default:
+ /* remove password attributes */
+ for (i = 0; data->password_attrs[i]; i++) {
+ ldb_msg_remove_attr(ares->message, data->password_attrs[i]);
+ }
+ }
+ }
+
+ if ((ac->allowedAttributes || ac->allowedAttributesEffective
+ || ac->allowedChildClasses || ac->allowedChildClassesEffective) &&
+ (!ldb_attr_in_list(ac->attrs, "objectClass") &&
+ !ldb_attr_in_list(ac->attrs, "*"))) {
+ ldb_msg_remove_attr(ares->message, "objectClass");
+ }
+
+ return ac->up_callback(ldb, ac->up_context, ares);
+
+error:
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
}
-/* delete a transaction */
-static int kludge_acl_del_trans(struct ldb_module *module)
+static int kludge_acl_search(struct ldb_module *module, struct ldb_request *req)
{
- return ldb_next_del_trans(module);
+ struct kludge_acl_context *ac;
+ struct ldb_request *down_req;
+ struct kludge_private_data *data;
+ int ret, i;
+
+ req->handle = NULL;
+
+ ac = talloc(req, struct kludge_acl_context);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ data = talloc_get_type(module->private_data, struct kludge_private_data);
+
+ ac->module = module;
+ ac->up_context = req->context;
+ ac->up_callback = req->callback;
+ 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;
+ }
+
+ 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->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) {
+ down_req->op.search.attrs
+ = ldb_attr_list_copy_add(down_req, down_req->op.search.attrs, "objectClass");
+ }
+
+ /* 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',
+ just as we would not allow that attribute to be returned */
+ switch (ac->user_type) {
+ case SYSTEM:
+ break;
+ default:
+ /* 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,
+ 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;
+ }
+
+ return ret;
}
-static int kludge_acl_request(struct ldb_module *module, struct ldb_request *req)
+/* ANY change type */
+static int kludge_acl_change(struct ldb_module *module, struct ldb_request *req)
{
- switch (req->operation) {
-
- case LDB_REQ_SEARCH:
- return kludge_acl_search(module, req);
- case LDB_REQ_REGISTER:
+ enum user_is user_type = what_is_user(module);
+ switch (user_type) {
+ case SYSTEM:
+ case ADMINISTRATOR:
return ldb_next_request(module, req);
default:
- /* anything else must be a change of some kind */
- return kludge_acl_change(module, req);
+ 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;
}
}
{
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;
return LDB_ERR_OPERATIONS_ERROR;
}
- ret = ldb_search(module->ldb, ldb_dn_explode(mem_ctx, "@KLUDGEACL"),
+ ret = ldb_search(module->ldb, ldb_dn_new(mem_ctx, module->ldb, "@KLUDGEACL"),
LDB_SCOPE_BASE,
NULL, attrs,
&res);
static const struct ldb_module_ops kludge_acl_ops = {
.name = "kludge_acl",
- .request = kludge_acl_request,
- .start_transaction = kludge_acl_start_trans,
- .end_transaction = kludge_acl_end_trans,
- .del_transaction = kludge_acl_del_trans,
+ .search = kludge_acl_search,
+ .add = kludge_acl_change,
+ .modify = kludge_acl_change,
+ .del = kludge_acl_change,
+ .rename = kludge_acl_change,
+ .extended = kludge_acl_change,
.init_context = kludge_acl_init
};