Initial Implementation of the DS objects access checks.
authorNadezhda Ivanova <nadezhda.ivanova@postpath.com>
Tue, 22 Sep 2009 00:27:50 +0000 (17:27 -0700)
committerNadezhda Ivanova <nadezhda.ivanova@postpath.com>
Tue, 22 Sep 2009 00:27:50 +0000 (17:27 -0700)
Currently disabled. The search will be greatly modified,
also the object tree stuff will be simplified.

source4/dsdb/samdb/ldb_modules/acl.c [new file with mode: 0644]
source4/dsdb/samdb/ldb_modules/config.mk
source4/dsdb/samdb/ldb_modules/kludge_acl.c
source4/dsdb/schema/schema_query.c
source4/libcli/security/access_check.c
source4/libcli/security/config.mk
source4/libcli/security/object_tree.c [new file with mode: 0644]
source4/libcli/security/security.h
source4/scripting/python/samba/provision.py

diff --git a/source4/dsdb/samdb/ldb_modules/acl.c b/source4/dsdb/samdb/ldb_modules/acl.c
new file mode 100644 (file)
index 0000000..1b02abc
--- /dev/null
@@ -0,0 +1,1151 @@
+/*
+   ldb database library
+
+   Copyright (C) Simo Sorce 2006-2008
+   Copyright (C) Nadezhda Ivanova 2009
+   Copyright (C) Anatoliy Atanasov  2009
+
+    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 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ *  Name: ldb
+ *
+ *  Component: ldb ACL module
+ *
+ *  Description: Module that performs authorisation access checks based on the
+ *               account's security context and the DACL of the object being polled.
+ *               Only DACL checks implemented at this point
+ *
+ *  Authors: Nadezhda Ivanova, Anatoliy Atanasov
+ */
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "auth/auth.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "dsdb/samdb/samdb.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "param/param.h"
+
+/* acl_search helper */
+struct acl_context {
+
+       struct ldb_module *module;
+       struct ldb_request *req;
+       struct ldb_request *down_req;
+
+       /*needed if we have to identify if this is SYSTEM_USER*/
+       enum security_user_level user_type;
+
+       uint32_t access_needed;
+       struct ldb_dn * dn_to_check;
+
+       /* set to true when we need to process the request as a SYSTEM_USER, regardless
+        * of the user's actual rights - for example when we need to retrieve the
+        * ntSecurityDescriptor */
+       bool ignore_security;
+       struct security_token *token;
+       /*needed to identify if we have requested these attributes*/
+       bool nTSecurityDescriptor;
+       bool objectClass;
+       int sec_result;
+};
+
+struct extended_access_check_attribute {
+       const char *oa_name;
+       const uint32_t requires_rights;
+};
+
+struct acl_private{
+       bool perform_check;
+};
+
+static int acl_search_callback(struct ldb_request *req, struct ldb_reply *ares);
+
+/*FIXME: Perhaps this should go in the .idl file*/
+#define SEC_GENERIC_ACCESS_NEVER_GRANTED ( 0xFFFFFFFF )
+
+/*Contains a part of the attributes - the ones that have predefined required rights*/
+static const struct extended_access_check_attribute extended_access_checks_table[] =
+{
+       {
+               .oa_name = "nTSecurityDescriptor",
+               .requires_rights = SEC_FLAG_SYSTEM_SECURITY & SEC_STD_READ_CONTROL,
+       },
+       {
+                .oa_name = "pekList",
+                .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED,
+        },
+       {
+                .oa_name = "currentValue",
+                .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED,
+       },
+       {
+                .oa_name = "dBCSPwd",
+                .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED,
+       },
+       {
+                .oa_name = "unicodePwd",
+                .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED,
+       },
+       {
+                .oa_name = "ntPwdHistory",
+                .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED,
+       },
+       {
+                .oa_name = "priorValue",
+                .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED,
+       },
+       {
+                .oa_name = "supplementalCredentials",
+                .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED,
+       },
+       {
+                .oa_name = "trustAuthIncoming",
+                .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED,
+       },
+       {
+                .oa_name = "trustAuthOutgoing",
+                .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED,
+       },
+       {
+                .oa_name = "ImPwdHistory",
+                .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED,
+       },
+       {
+                .oa_name = "initialAuthIncoming",
+                .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED,
+       },
+       {
+                .oa_name = "initialAuthOutgoing",
+                .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED,
+       },
+       {
+                .oa_name = "msDS-ExecuteScriptPassword",
+                .requires_rights = SEC_GENERIC_ACCESS_NEVER_GRANTED,
+       },
+};
+
+static NTSTATUS extended_access_check(const char *attribute_name, const int access_rights, uint32_t searchFlags)
+{
+       int i = 0;
+       if (access_rights == SEC_GENERIC_ACCESS_NEVER_GRANTED) {
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       /*Check if the attribute is in the table first*/
+       for ( i = 0; extended_access_checks_table[i].oa_name; i++ ) {
+               if (ldb_attr_cmp(extended_access_checks_table[i].oa_name, attribute_name) == 0) {
+                       if ((access_rights & extended_access_checks_table[i].requires_rights) == access_rights) {
+                               return NT_STATUS_OK;
+                       } else {
+                               return NT_STATUS_ACCESS_DENIED;
+                       }
+               }
+       }
+
+       /*Check for attribute whose attributeSchema has 0x80 set in searchFlags*/
+       if ((searchFlags & SEARCH_FLAG_CONFIDENTIAL) == SEARCH_FLAG_CONFIDENTIAL) {
+               if (((SEC_ADS_READ_PROP & SEC_ADS_CONTROL_ACCESS) & access_rights) == access_rights) {
+                       return NT_STATUS_OK;
+               } else {
+                       return NT_STATUS_ACCESS_DENIED;
+               }
+       }
+
+       /*Check attributes with *special* behaviour*/
+       if (ldb_attr_cmp("msDS-QuotaEffective", attribute_name) == 0 || ldb_attr_cmp("msDS-QuotaUsed", attribute_name) == 0){
+               /*Rights required:
+                *
+                *(RIGHT_DS_READ_PROPERTY on the Quotas container or
+                *((the client is querying the quota for the security principal it is authenticated as) and
+                *(DS-Query-Self-Quota control access right on the Quotas container))
+                */
+       }
+
+        if (ldb_attr_cmp("userPassword", attribute_name) == 0) {
+               /*When the dSHeuristics.fUserPwdSupport flag is false, the requester must be granted RIGHT_DS_READ_PROPERTY.
+                *When the dSHeuristics.fUserPwdSupport flag is true, access is never granted.
+                */
+       }
+
+       if (ldb_attr_cmp("sDRightsEffective", attribute_name) == 0) {
+               /*FIXME:3.1.1.4.5.4 in MS-ADTS*/
+       }
+
+       if (ldb_attr_cmp("allowedChildClassesEffective", attribute_name) == 0) {
+               /*FIXME:3.1.1.4.5.5 in MS-ADTS*/
+        }
+
+       if (ldb_attr_cmp("allowedAttributesEffective", attribute_name) == 0) {
+               /*FIXME:3.1.1.4.5.7 in MS-ADTS*/
+        }
+
+       if (ldb_attr_cmp("msDS-Approx-Immed-Subordinates", attribute_name) == 0) {
+               /*FIXME:3.1.1.4.5.15 in MS-ADTS*/
+        }
+
+       if (ldb_attr_cmp("msDS-QuotaEffective", attribute_name) == 0) {
+               /*FIXME:3.1.1.4.5.22 in MS-ADTS*/
+        }
+
+       if (ldb_attr_cmp("msDS-ReplAttributeMetaData", attribute_name) == 0 || ldb_attr_cmp("msDS-ReplAttributeMetaData", attribute_name) == 0) {
+               /*The security context of the requester must be granted the following rights on the replPropertyMetaData attribute:
+                *(RIGHT_DS_READ_PROPERTY)or (DS-Replication-Manage-Topology by ON!nTSecurityDescriptor)
+                */
+        }
+
+       if (ldb_attr_cmp("msDS-NCReplInboundNeighbors", attribute_name) == 0) {
+               /*The security context of the requester must be granted the following rights on repsFrom:
+                *(RIGHT_DS_READ_PROPERTY) or (DS-Replication-Manage-Topology) or (DS-Replication-Monitor-Topology)
+                */
+        }
+
+       if (ldb_attr_cmp("msDS-NCReplOutboundNeighbors", attribute_name) == 0) {
+               /*The security context of the requester must be granted the following rights on repsTo:
+                *(RIGHT_DS_READ_PROPERTY) or (DS-Replication-Manage-Topology) or (DS-Replication-Monitor-Topology)
+                */
+        }
+
+       if (ldb_attr_cmp("msDS-NCReplCursors", attribute_name) == 0) {
+               /*The security context of the requester must be granted the following rights on replUpToDateVector: (RIGHT_DS_READ_PROPERTY)
+                *or (DS-Replication-Manage-Topology) or (DS-Replication-Monitor-Topology)
+                */
+        }
+
+       if (ldb_attr_cmp("msDS-IsUserCachableAtRodc", attribute_name) == 0) {
+               /*The security context of the requester must be granted
+                *the DS-Replication-Secrets-Synchronize control access right on the root of the default NC.
+                */
+        }
+
+       return NT_STATUS_OK;
+}
+
+/* Builds an object tree for object specific access checks */
+static struct object_tree * build_object_tree_form_attr_list(TALLOC_CTX *mem_ctx,   /* Todo this context or separate? */
+                                                            struct ldb_context *ldb,
+                                                            const char ** attr_names,
+                                                            int num_attrs,
+                                                            const char * object_class,
+                                                            uint32_t init_access)
+{
+       const struct dsdb_schema *schema = dsdb_get_schema(ldb);
+       const struct GUID *oc_guid = class_schemaid_guid_by_lDAPDisplayName(schema, object_class);
+       struct object_tree *tree;
+       int i;
+
+       if (!oc_guid)
+               return NULL;
+
+       tree = insert_in_object_tree(mem_ctx, oc_guid, NULL, init_access, NULL);
+       if (attr_names){
+               for (i=0; i < num_attrs; i++){
+                       const struct dsdb_attribute *attribute = dsdb_attribute_by_lDAPDisplayName(schema,attr_names[i]);
+                       if (attribute)
+                               insert_in_object_tree(mem_ctx,
+                                                     &attribute->schemaIDGUID,
+                                                     &attribute->attributeSecurityGUID,
+                                                     init_access,
+                                                     tree);
+               }
+       }
+       return tree;
+}
+
+bool is_root_base_dn(struct ldb_context *ldb, struct ldb_dn *dn_to_check)
+{
+       int result;
+       struct ldb_dn *root_base_dn = ldb_get_root_basedn(ldb);
+       result = ldb_dn_compare(root_base_dn,dn_to_check);
+       return (result==0);
+}
+
+static int acl_op_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+       struct acl_context *ac;
+
+       ac = talloc_get_type(req->context, struct acl_context);
+
+       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);
+       }
+
+       if (ares->type != LDB_REPLY_DONE) {
+               talloc_free(ares);
+               return ldb_module_done(ac->req, NULL, NULL,
+                                       LDB_ERR_OPERATIONS_ERROR);
+       }
+
+       return ldb_module_done(ac->req, ares->controls,
+                               ares->response, ares->error);
+}
+
+
+static int acl_access_check_add(struct ldb_reply *ares,
+                               struct acl_context *ac,
+                               struct security_descriptor *sd)
+{
+       uint32_t access_granted = 0;
+       NTSTATUS status;
+       struct ldb_dn *parent;
+       struct ldb_dn *grandparent;
+       struct object_tree *tree = NULL;
+
+       parent = ldb_dn_get_parent(ac->req, ac->req->op.add.message->dn);
+       grandparent = ldb_dn_get_parent(ac->req, parent);
+       if (ldb_dn_compare(ares->message->dn, grandparent) == 0)
+               status = sec_access_check_ds(sd, ac->token,
+                                            SEC_ADS_LIST,
+                                            &access_granted,
+                                            NULL);
+       else if (ldb_dn_compare(ares->message->dn, parent) == 0){
+               struct ldb_message_element *oc_el;
+               struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
+               const struct dsdb_schema *schema = dsdb_get_schema(ldb);
+               int i;
+
+               oc_el = ldb_msg_find_element(ares->message, "objectClass");
+               if (!oc_el || oc_el->num_values == 0)
+                       return LDB_SUCCESS;
+               for (i = 0; i < oc_el->num_values; i++){
+                       const struct GUID *guid = class_schemaid_guid_by_lDAPDisplayName(schema,
+                                                                                         oc_el->values[i].data);
+                       ac->sec_result = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+                       tree = insert_in_object_tree(ac->req, guid, NULL, SEC_ADS_CREATE_CHILD,
+                                                    tree);
+                       status = sec_access_check_ds(sd, ac->token, SEC_ADS_CREATE_CHILD,&access_granted, tree);
+                       if (NT_STATUS_IS_OK(status))
+                               ac->sec_result = LDB_SUCCESS;
+               }
+       }
+       else
+               return LDB_SUCCESS;
+
+       return ac->sec_result;
+}
+
+static int acl_access_check_modify(struct ldb_reply *ares, struct acl_context *ac,
+                                  struct security_descriptor *sd)
+{
+       uint32_t access_granted = 0;
+       NTSTATUS status;
+       struct ldb_dn *parent;
+       struct object_tree *tree = NULL;
+
+       parent = ldb_dn_get_parent(ac->req, ac->req->op.add.message->dn);
+       if (ldb_dn_compare(ares->message->dn, parent) == 0)
+               status = sec_access_check_ds(sd, ac->token, SEC_ADS_LIST,&access_granted, NULL);
+       else if (ldb_dn_compare(ares->message->dn, ac->req->op.add.message->dn) == 0){
+               struct ldb_message_element *oc_el;
+               struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
+               const struct dsdb_schema *schema = dsdb_get_schema(ldb);
+               int i;
+               struct GUID *guid;
+               oc_el = ldb_msg_find_element(ares->message, "objectClass");
+               if (!oc_el || oc_el->num_values == 0)
+                       return LDB_SUCCESS;
+
+               guid = class_schemaid_guid_by_lDAPDisplayName(schema,
+                                                             oc_el->values[oc_el->num_values-1].data);
+               tree = insert_in_object_tree(ac->req, guid, NULL, SEC_ADS_WRITE_PROP,
+                                                    tree);
+               for (i=0; i < ac->req->op.mod.message->num_elements; i++){
+                       const struct dsdb_attribute *attr = dsdb_attribute_by_lDAPDisplayName(schema,
+                                                                                             ac->req->op.mod.message->elements[i].name);
+                       if (!attr)
+                               return LDB_ERR_OPERATIONS_ERROR; /* What should we actually return here? */
+                       insert_in_object_tree(ac, &attr->schemaIDGUID,
+                                             &attr->attributeSecurityGUID, ac->access_needed, tree);
+               }
+               status = sec_access_check_ds(sd, ac->token, SEC_ADS_WRITE_PROP ,&access_granted, tree);
+               if (!NT_STATUS_IS_OK(status))
+                       ac->sec_result = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+       }
+       else
+               return LDB_SUCCESS;
+       return ac->sec_result;
+}
+/*TODO*/
+static int acl_access_check_rename(struct ldb_reply *ares, struct acl_context *ac,
+                                  struct security_descriptor *sd)
+{
+       return ac->sec_result;
+}
+
+static int acl_access_check_delete(struct ldb_reply *ares, struct acl_context *ac,
+                                  struct security_descriptor *sd)
+{
+       uint32_t access_granted = 0;
+       NTSTATUS status;
+       struct ldb_dn *parent;
+       struct object_tree *tree = NULL;
+
+       parent = ldb_dn_get_parent(ac->req, ac->req->op.del.dn);
+       if (ldb_dn_compare(ares->message->dn, parent) == 0){
+               status = sec_access_check_ds(sd, ac->token, SEC_ADS_LIST,&access_granted, NULL);
+               if (!NT_STATUS_IS_OK(status)){
+                       ac->sec_result = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+                       return ac->sec_result;
+               }
+               status = sec_access_check_ds(sd, ac->token, SEC_ADS_DELETE_CHILD,&access_granted, NULL);
+               if (NT_STATUS_IS_OK(status)){
+                       ac->sec_result = LDB_SUCCESS;
+                       return ac->sec_result;
+               }
+       }
+       else if (ldb_dn_compare(ares->message->dn, ac->req->op.del.dn) == 0){
+               status = sec_access_check_ds(sd, ac->token, SEC_STD_DELETE, &access_granted, NULL);
+               if (!NT_STATUS_IS_OK(status))
+                       ac->sec_result = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+       }
+       return ac->sec_result;
+}
+
+static int acl_access_check_search(struct ldb_reply *ares, struct acl_context *ac,
+                                  struct security_descriptor *sd)
+{
+       uint32_t access_granted;
+       NTSTATUS status;
+       struct ldb_dn *parent;
+
+       if (ac->user_type == SECURITY_SYSTEM || ac->user_type == SECURITY_ANONYMOUS) {
+               return LDB_SUCCESS;/*FIXME: we have anonymous access*/
+       }
+
+       parent = ldb_dn_get_parent(ac->req, ac->dn_to_check);
+       ac->sec_result = LDB_SUCCESS;
+       if (ldb_dn_compare(ares->message->dn, parent) == 0) {
+               status = sec_access_check_ds(sd, ac->token, SEC_ADS_LIST,&access_granted, NULL);
+               if (!NT_STATUS_IS_OK(status)) {
+                       ac->sec_result = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+               }
+       }
+
+       return ac->sec_result;
+}
+
+static int acl_perform_access_check(struct ldb_request *req, struct ldb_reply *ares,
+                                   struct acl_context *ac)
+{
+       struct ldb_message_element *oc_el;
+       struct security_descriptor *sd;
+       enum ndr_err_code ndr_err;
+
+       oc_el = ldb_msg_find_element(ares->message, "ntSecurityDescriptor");
+       if (!oc_el || oc_el->num_values == 0)
+               return LDB_SUCCESS;
+
+       sd = talloc(ac, struct security_descriptor);
+       if(!sd) {
+               return ldb_module_done(ac->req, ares->controls,
+                                      ares->response, LDB_ERR_OPERATIONS_ERROR);
+       }
+       ndr_err = ndr_pull_struct_blob(&oc_el->values[0], sd, NULL, sd,
+                                      (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err))
+               return ldb_module_done(ac->req, ares->controls,
+                                      ares->response, LDB_ERR_OPERATIONS_ERROR);
+       switch (ac->req->operation) {
+       case LDB_SEARCH:
+               return acl_access_check_search(ares, ac, sd);
+       case LDB_ADD:
+               return acl_access_check_add(ares, ac, sd);
+       case LDB_MODIFY:
+               return acl_access_check_modify(ares, ac, sd);
+       case LDB_DELETE:
+               return acl_access_check_delete(ares, ac, sd);
+       case LDB_RENAME:
+               return acl_access_check_rename(ares, ac, sd);
+       default:
+               return ldb_module_done(ac->req, ares->controls,
+                                      ares->response, LDB_ERR_OPERATIONS_ERROR);
+       }
+       return LDB_SUCCESS;
+}
+
+static int acl_forward_add(struct ldb_reply *ares,
+                          struct acl_context *ac)
+{
+  struct ldb_request *newreq;
+       struct ldb_context *ldb;
+       int ret;
+
+       ldb = ldb_module_get_ctx(ac->module);
+       ret = ldb_build_add_req(&newreq,ldb,
+                               ac,
+                               ac->req->op.add.message,
+                               ac->req->controls,
+                               ac,
+                               acl_op_callback,
+                               ac->req);
+       if (ret != LDB_SUCCESS)
+               return ldb_module_done(ac->req, ares->controls,
+                                      ares->response, LDB_ERR_OPERATIONS_ERROR);
+       return ldb_next_request(ac->module, newreq);
+}
+
+static int acl_forward_modify(struct ldb_reply *ares,
+                             struct acl_context *ac)
+{
+  struct ldb_request *newreq;
+       struct ldb_context *ldb;
+       int ret;
+
+       ldb = ldb_module_get_ctx(ac->module);
+       ret = ldb_build_mod_req(&newreq,ldb,
+                                   ac,
+                                   ac->req->op.mod.message,
+                                   ac->req->controls,
+                                   ac,
+                                   acl_op_callback,
+                                   ac->req);
+       if (ret != LDB_SUCCESS)
+               return ldb_module_done(ac->req, ares->controls,
+                                      ares->response, LDB_ERR_OPERATIONS_ERROR);
+       return ldb_next_request(ac->module, newreq);
+}
+
+static int acl_forward_delete(struct ldb_reply *ares,
+                             struct acl_context *ac)
+{
+       struct ldb_request *newreq;
+       struct ldb_context *ldb;
+       int ret;
+
+       ldb = ldb_module_get_ctx(ac->module);
+       ret = ldb_build_del_req(&newreq, ldb,
+                                   ac,
+                                   ac->req->op.del.dn,
+                                   ac->req->controls,
+                                   ac,
+                                   acl_op_callback,
+                                   ac->req);
+       if (ret != LDB_SUCCESS)
+               return ldb_module_done(ac->req, ares->controls,
+                                      ares->response, LDB_ERR_OPERATIONS_ERROR);
+       return ldb_next_request(ac->module, newreq);
+}
+
+static int acl_forward_rename(struct ldb_reply *ares,
+                             struct acl_context *ac)
+{
+       return LDB_SUCCESS;
+}
+
+static int acl_forward_search(struct acl_context *ac)
+{
+       int ret;
+       const char * const *attrs;
+       struct ldb_control *sd_control;
+       struct ldb_control **sd_saved_controls;
+       struct ldb_context *ldb;
+       struct ldb_request *newreq;
+
+       ldb = ldb_module_get_ctx(ac->module);
+       attrs = ac->req->op.search.attrs;
+       if (attrs) {
+               ac->nTSecurityDescriptor = false;
+               ac->objectClass = false;
+               if (!ldb_attr_in_list(ac->req->op.search.attrs, "nTSecurityDescriptor")) {
+                       attrs = ldb_attr_list_copy_add(ac, attrs, "nTSecurityDescriptor");
+                       ac->nTSecurityDescriptor = true;
+               }
+               if (!ldb_attr_in_list(ac->req->op.search.attrs, "objectClass")) {
+                       attrs = ldb_attr_list_copy_add(ac, attrs, "objectClass");
+                       ac->objectClass = true;
+               }
+       }
+       ret = ldb_build_search_req_ex(&newreq,ldb,
+                                     ac,
+                                     ac->req->op.search.base,
+                                     ac->req->op.search.scope,
+                                     ac->req->op.search.tree,
+                                     attrs,
+                                     ac->req->controls,
+                                     ac, acl_search_callback,
+                                     ac->req);
+       if (ret != LDB_SUCCESS) {
+                return LDB_ERR_OPERATIONS_ERROR;
+       }
+       /* check if there's an SD_FLAGS control */
+       sd_control = ldb_request_get_control(newreq, 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, newreq, &sd_saved_controls)) {
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+       }
+       return ldb_next_request(ac->module, newreq);
+}
+
+static int acl_forward_request(struct ldb_reply *ares,
+                              struct acl_context *ac)
+{
+       switch (ac->req->operation) {
+       case LDB_SEARCH:
+               return acl_forward_search(ac);
+       case LDB_ADD:
+               return acl_forward_add(ares,ac);
+       case LDB_MODIFY:
+               return acl_forward_modify(ares,ac);
+       case LDB_DELETE:
+               return acl_forward_delete(ares,ac);
+       case LDB_RENAME:
+               return acl_forward_rename(ares,ac);
+       default:
+               return ldb_module_done(ac->req, ares->controls,
+                                      ares->response, LDB_ERR_OPERATIONS_ERROR);
+       }
+       return LDB_SUCCESS;
+}
+
+static int acl_visible_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+       struct acl_context *ac;
+
+       ac = talloc_get_type(req->context, struct acl_context);
+
+       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:
+               return acl_perform_access_check(req, ares, ac);
+       case LDB_REPLY_REFERRAL:
+               return ldb_module_send_referral(ac->req, ares->referral); /* what to do here actually? */
+       case LDB_REPLY_DONE:
+               if (ac->sec_result != LDB_SUCCESS) {
+                       return ldb_module_done(ac->req, ares->controls,
+                                               ares->response, ac->sec_result);
+               }
+               return acl_forward_request(ares,ac);
+       default:
+               break;
+       }
+       return LDB_SUCCESS;
+}
+
+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
+               = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
+       return security_session_user_level(session_info);
+}
+
+static struct security_token * user_token(struct ldb_module *module)
+{
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+       struct auth_session_info *session_info
+               = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
+       if(!session_info) {
+               return NULL;
+       }
+       return session_info->security_token;
+}
+
+
+static int make_req_access_check(struct ldb_module *module, struct ldb_request *req,
+                                struct acl_context *ac, const char *filter)
+{
+       struct ldb_context *ldb;
+       int ret;
+       const char **attrs = talloc_array(ac, const char *, 3);
+       struct ldb_parse_tree *tree = ldb_parse_tree(req, filter);
+
+       attrs[0] = talloc_strdup(attrs, "ntSecurityDescriptor");
+       attrs[1] = talloc_strdup(attrs, "objectClass");
+       attrs[2] = NULL;
+
+       ldb = ldb_module_get_ctx(module);
+       ret = ldb_build_search_req_ex(&ac->down_req,
+                                     ldb, ac,
+                                     ac->dn_to_check,
+                                     LDB_SCOPE_SUBTREE,
+                                     tree,
+                                     attrs,
+                                     NULL,
+                                     ac, acl_visible_callback,
+                                     req);
+       return ret;
+}
+
+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
+               = (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
+       if (!session_info) {
+               return "UNKNOWN (NULL)";
+       }
+
+       return talloc_asprintf(mem_ctx, "%s\\%s",
+                              session_info->server_info->domain_name,
+                              session_info->server_info->account_name);
+}
+
+static int acl_module_init(struct ldb_module *module)
+{
+       struct ldb_context *ldb;
+       struct acl_private *data;
+       int ret;
+
+       ldb = ldb_module_get_ctx(module);
+
+       ret = ldb_mod_register_control(module, LDB_CONTROL_SD_FLAGS_OID);
+       if (ret != LDB_SUCCESS) {
+               ldb_debug(ldb, LDB_DEBUG_ERROR,
+                         "acl_module_init: Unable to register control with rootdse!\n");
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       data = talloc(module, struct acl_private);
+       data->perform_check = lp_parm_bool(ldb_get_opaque(ldb, "loadparm"),
+                                 NULL, "acl", "perform", false);
+       ldb_module_set_private(module, data);
+
+       return ldb_next_init(module);
+}
+
+static int acl_add(struct ldb_module *module, struct ldb_request *req)
+{
+       int ret;
+       struct acl_context *ac;
+       struct ldb_dn * parent = ldb_dn_get_parent(req, req->op.add.message->dn);
+       char * filter;
+       struct ldb_context *ldb;
+       struct acl_private *data;
+
+       ldb = ldb_module_get_ctx(module);
+       data = talloc_get_type(ldb_module_get_private(module), struct acl_private);
+
+       if (!data->perform_check)
+               return ldb_next_request(module, req);
+
+       ac = talloc(req, struct acl_context);
+       if (ac == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (what_is_user(module) == SECURITY_SYSTEM)
+               return ldb_next_request(module, req);
+
+       ac->module = module;
+       ac->req = req;
+       ac->ignore_security = true;
+       ac->dn_to_check = ldb_dn_get_parent(req, parent);
+       ac->token = user_token(module);
+       ac->user_type = what_is_user(module);
+       ac->sec_result = LDB_SUCCESS;
+       if (!is_root_base_dn(ldb, req->op.add.message->dn) && parent && !is_root_base_dn(ldb, parent)){
+               filter = talloc_asprintf(req,"(&(objectClass=*)(|(%s=%s)(%s=%s))))",
+                                        ldb_dn_get_component_name(parent,0),
+                                        ldb_dn_get_component_val(parent,0)->data,
+                                        ldb_dn_get_component_name(ac->dn_to_check,0),
+                                        ldb_dn_get_component_val(ac->dn_to_check,0)->data);
+
+               ret = make_req_access_check(module, req, ac, filter);
+               if (ret != LDB_SUCCESS){
+                       return ret;
+               }
+               return ldb_next_request(module, ac->down_req);
+       }
+       return ldb_next_request(module, req);
+}
+
+static int acl_modify(struct ldb_module *module, struct ldb_request *req)
+{
+       int ret;
+       struct acl_context *ac;
+       struct ldb_dn * parent = ldb_dn_get_parent(req, req->op.mod.message->dn);
+       char * filter;
+       struct ldb_context *ldb;
+       struct acl_private *data;
+
+       ldb = ldb_module_get_ctx(module);
+       data = talloc_get_type(ldb_module_get_private(module), struct acl_private);
+
+       if (!data->perform_check)
+               return ldb_next_request(module, req);
+
+       ac = talloc(req, struct acl_context);
+       if (ac == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+/*     if (what_is_user(module) == SECURITY_SYSTEM) */
+               return ldb_next_request(module, req);
+
+       ac->module = module;
+       ac->req = req;
+       ac->ignore_security = true;
+       ac->dn_to_check = req->op.mod.message->dn;
+       ac->token = user_token(module);
+       ac->user_type = what_is_user(module);
+       ac->sec_result = LDB_SUCCESS;
+       if (!is_root_base_dn(ldb, req->op.mod.message->dn) && parent && !is_root_base_dn(ldb, parent)){
+         filter = talloc_asprintf(req,"(&(objectClass=*)(|(%s=%s)(%s=%s))))",
+                                  ldb_dn_get_component_name(parent,0),
+                                  ldb_dn_get_component_val(parent,0)->data,
+                                  ldb_dn_get_component_name(req->op.mod.message->dn,0),
+                                  ldb_dn_get_component_val(req->op.mod.message->dn,0)->data);
+
+               ret = make_req_access_check(module, req, ac, filter);
+               if (ret != LDB_SUCCESS){
+                       return ret;
+               }
+               return ldb_next_request(module, ac->down_req);
+       }
+       return ldb_next_request(module, req);
+}
+
+/* similar to the modify for the time being.
+ * We need to concider the special delete tree case, though - TODO */
+static int acl_delete(struct ldb_module *module, struct ldb_request *req)
+{
+       int ret;
+       struct acl_context *ac;
+       struct ldb_dn * parent = ldb_dn_get_parent(req, req->op.del.dn);
+       char * filter;
+       struct ldb_context *ldb;
+       struct acl_private *data;
+
+       ldb = ldb_module_get_ctx(module);
+       data = talloc_get_type(ldb_module_get_private(module), struct acl_private);
+
+       if (!data->perform_check)
+               return ldb_next_request(module, req);
+
+       ac = talloc(req, struct acl_context);
+       if (ac == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (ac->user_type == SECURITY_SYSTEM)
+               return ldb_next_request(module, req);
+
+       ac->module = module;
+       ac->req = req;
+       ac->ignore_security = true;
+       ac->dn_to_check = req->op.del.dn;
+       ac->token = user_token(module);
+       ac->user_type = what_is_user(module);
+       ac->sec_result = LDB_SUCCESS;
+       if (parent) {
+               filter = talloc_asprintf(req,"(&(objectClass=*)(|(%s=%s)(%s=%s))))",
+                                        ldb_dn_get_component_name(parent,0),
+                                        ldb_dn_get_component_val(parent,0)->data,
+                                        ldb_dn_get_component_name(req->op.del.dn,0),
+                                        ldb_dn_get_component_val(req->op.del.dn,0)->data);
+               ret = make_req_access_check(module, req, ac, filter);
+
+               if (ret != LDB_SUCCESS){
+                       return ret;
+                }
+               return ldb_next_request(module, ac->down_req);
+       }
+
+       return ldb_next_request(module, req);
+}
+
+static int acl_rename(struct ldb_module *module, struct ldb_request *req)
+{
+       struct ldb_dn *source_parent;
+       struct ldb_dn *dest_parent;
+       int ret;
+       struct acl_context *ac;
+       char * filter;
+       struct ldb_context *ldb;
+       struct acl_private *data;
+
+       ldb = ldb_module_get_ctx(module);
+       data = talloc_get_type(ldb_module_get_private(module), struct acl_private);
+
+       if (!data->perform_check)
+               return ldb_next_request(module, req);
+
+       ac = talloc(req, struct acl_context);
+       if (ac == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       if (ac->user_type == SECURITY_SYSTEM)
+               return ldb_next_request(module, req);
+
+       ac->module = module;
+       ac->req = req;
+       ac->ignore_security = true;
+       ac->token = user_token(module);
+       ac->user_type = what_is_user(module);
+       ac->sec_result = LDB_SUCCESS;
+
+       /* We need to know if it is a simple rename or a move operation */
+       source_parent = ldb_dn_get_parent(req, req->op.rename.olddn);
+       dest_parent = ldb_dn_get_parent(req, req->op.rename.newdn);
+
+       if (ldb_dn_compare(source_parent, dest_parent) == 0){
+               /*Not a move, just rename*/
+               filter = talloc_asprintf(req,"(&(objectClass=*)(|(%s=%s)(%s=%s))))",
+                                        ldb_dn_get_component_name(dest_parent,0),
+                                        ldb_dn_get_component_val(dest_parent,0)->data,
+                                        ldb_dn_get_component_name(req->op.rename.olddn,0),
+                                        ldb_dn_get_component_val(req->op.rename.olddn,0)->data);
+       }
+       else{
+               filter = talloc_asprintf(req,"(&(objectClass=*)(|(%s=%s)(%s=%s))))",
+                                        ldb_dn_get_component_name(dest_parent,0),
+                                        ldb_dn_get_component_val(dest_parent,0)->data,
+                                        ldb_dn_get_component_name(source_parent,0),
+                                        ldb_dn_get_component_val(source_parent,0)->data);
+       }
+
+       ret = make_req_access_check(module, req, ac, filter);
+
+       if (ret != LDB_SUCCESS){
+               return ret;
+               }
+       return ldb_next_request(module, ac->down_req);
+}
+
+static int acl_search_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+       struct ldb_context *ldb;
+       struct acl_context *ac;
+       struct security_descriptor *sd;
+       uint32_t searchFlags;
+       uint32_t access_mask;
+       struct object_tree *ot;
+       int i, ret;
+       NTSTATUS status;
+       struct ldb_message_element *element_security_descriptor;
+       struct ldb_message_element *element_object_class;
+       const struct dsdb_attribute *attr;
+       const struct dsdb_schema *schema;
+       struct GUID *oc_guid;
+
+       ac = talloc_get_type(req->context, struct acl_context);
+       ldb = ldb_module_get_ctx(ac->module);
+       schema = dsdb_get_schema(ldb);
+       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:
+               switch (ac->user_type) {
+               case SECURITY_SYSTEM:
+               case SECURITY_ANONYMOUS:/*FIXME: should we let anonymous have system access*/
+                       break;
+               default:
+                       /* Access checks
+                        *
+                        * 0. If we do not have nTSecurityDescriptor, we do not have an object in the response,
+                        *    so check the parent dn.
+                        * 1. Call sec_access_check on empty tree
+                        * 2. For each attribute call extended_access_check
+                        * 3. For each attribute call build_object_tree_form_attr_list and then check with sec_access_check
+                        *
+                        */
+                       element_security_descriptor = ldb_msg_find_element(ares->message, "nTSecurityDescriptor");
+                       element_object_class = ldb_msg_find_element(ares->message, "objectClass");
+                       if (!element_security_descriptor || !element_object_class)
+                               break;
+
+                       sd = talloc(ldb, struct security_descriptor);
+                       if(!sd) {
+                         return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+                       }
+                       if(!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_struct_blob(&element_security_descriptor->values[0],
+                                                                        ldb,
+                                                                        NULL,
+                                                                        sd,
+                                                                        (ndr_pull_flags_fn_t)ndr_pull_security_descriptor))) {
+                               DEBUG(0, ("acl_search_callback: Error parsing security descriptor\n"));
+                               return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+                       }
+
+                       oc_guid = class_schemaid_guid_by_lDAPDisplayName(schema, element_object_class->values[0].data);
+                       for (i=0; i<ares->message->num_elements; i++) {
+                               attr = dsdb_attribute_by_lDAPDisplayName(schema, ares->message->elements[i].name);
+                               if (attr) {
+                                       searchFlags = attr->searchFlags;
+                               } else {
+                                       searchFlags = 0x0;
+                               }
+
+                               /*status = extended_access_check(ares->message->elements[i].name, access_mask, searchFlags); */ /* Todo FIXME */
+                               ac->access_needed = SEC_ADS_READ_PROP;
+                               if (NT_STATUS_IS_OK(status)) {
+                                       ot = insert_in_object_tree(req, oc_guid, NULL, ac->access_needed, NULL);
+
+                                       insert_in_object_tree(req,
+                                                             &attr->schemaIDGUID,
+                                                             &attr->attributeSecurityGUID,
+                                                             ac->access_needed,
+                                                             ot);
+
+                                       status = sec_access_check_ds(sd,
+                                                                    ac->token,
+                                                                    ac->access_needed,
+                                                                    &access_mask,
+                                                                    ot);
+
+                                       if (NT_STATUS_IS_OK(status)) {
+                                               continue;
+                                       }
+                               }
+                               ldb_msg_remove_attr(ares->message, ares->message->elements[i].name);
+                       }
+                       break;
+               }
+               if (ac->nTSecurityDescriptor) {
+                       ldb_msg_remove_attr(ares->message, "nTSecurityDescriptor");
+               } else if (ac->objectClass) {
+                       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);
+
+       case LDB_REPLY_DONE:
+               return ldb_module_done(ac->req, ares->controls,ares->response, LDB_SUCCESS);
+       }
+
+        return LDB_SUCCESS;
+}
+
+static int acl_search(struct ldb_module *module, struct ldb_request *req)
+{
+       int ret;
+       struct ldb_context *ldb;
+       struct acl_context *ac;
+       const char **attrs;
+       struct ldb_control *sd_control;
+       struct ldb_control **sd_saved_controls;
+       struct ldb_dn * parent;
+       struct acl_private *data;
+
+       ldb = ldb_module_get_ctx(module);
+       data = talloc_get_type(ldb_module_get_private(module), struct acl_private);
+
+       if (!data || !data->perform_check)
+               return ldb_next_request(module, req);
+
+       if (what_is_user(module) == SECURITY_SYSTEM)
+               return ldb_next_request(module, req);
+
+       ac = talloc_get_type(req->context, struct acl_context);
+       if ( ac == NULL ) {
+               ac = talloc(req, struct acl_context);
+               if (ac == NULL) {
+                       ldb_oom(ldb);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               ac->module = module;
+               ac->req = req;
+               ac->ignore_security = false;
+               ac->user_type = what_is_user(module);
+               ac->token = user_token(module);
+               ac->dn_to_check = req->op.search.base;
+               ac->sec_result = LDB_SUCCESS;
+
+               attrs = talloc_array(ac, const char*, 2);
+               attrs[0] = talloc_strdup(attrs, "nTSecurityDescriptor");
+               attrs[1] = NULL;
+               parent = ldb_dn_get_parent(req, ac->dn_to_check);
+               if (!is_root_base_dn(ldb, req->op.search.base) && parent && !is_root_base_dn(ldb, parent)) {
+                       /*we have parent so check for visibility*/
+                       ret = ldb_build_search_req(&ac->down_req,
+                                                  ldb, ac,
+                                                  parent,
+                                                  LDB_SCOPE_BASE,
+                                                  "(objectClass=*)",
+                                                  attrs,
+                                                  req->controls,
+                                                  ac, acl_visible_callback,
+                                                  req);
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
+                       }
+                       return ldb_next_request(module, ac->down_req);
+               } else {
+                       return acl_forward_search(ac);
+               }
+       }
+
+       return ldb_next_request(module, req);
+}
+
+static int 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;
+       struct acl_private *data;
+
+       data = talloc_get_type(ldb_module_get_private(module), struct acl_private);
+
+       if (!data->perform_check)
+               return ldb_next_request(module, req);
+
+       /* 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,
+                                      "acl_extended: attempted database modify not permitted."
+                                      "User %s is not SYSTEM or an Administrator",
+                                      user_name(req, module));
+               return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS;
+       }
+}
+
+_PUBLIC_ const struct ldb_module_ops ldb_acl_module_ops = {
+       .name              = "acl",
+       .search            = acl_search,
+       .add               = acl_add,
+       .modify            = acl_modify,
+       .del               = acl_delete,
+       .rename            = acl_rename,
+       .extended          = acl_extended,
+       .init_context      = acl_module_init
+};
index 9384d062a4cde643ce59b3fbe59ed8c10228b1ee..a49b238591d608d616dc95081a50ac8bfdf41638 100644 (file)
@@ -357,3 +357,15 @@ INIT_FUNCTION = LDB_MODULE(resolve_oids)
 ################################################
 
 ldb_resolve_oids_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/resolve_oids.o
+
+################################################
+# Start MODULE ldb_acl
+[MODULE::ldb_acl]
+PRIVATE_DEPENDENCIES = LIBTALLOC LIBEVENTS LIBSECURITY SAMDB
+SUBSYSTEM = LIBLDB
+INIT_FUNCTION = LDB_MODULE(acl)
+
+# End MODULE ldb_acl
+################################################
+
+ldb_acl_OBJ_FILES = $(dsdbsrcdir)/samdb/ldb_modules/acl.o
index 15db491171f3e3045e6987be966ad054ac0965d9..34f848de8a4899d8fcd461429ef5dc9a869a081c 100644 (file)
@@ -35,6 +35,7 @@
 #include "auth/auth.h"
 #include "libcli/security/security.h"
 #include "dsdb/samdb/samdb.h"
+#include "param/param.h"
 
 /* Kludge ACL rules:
  *
@@ -46,6 +47,7 @@
 
 struct kludge_private_data {
        const char **password_attrs;
+       bool acl_perform;
 };
 
 static enum security_user_level what_is_user(struct ldb_module *module) 
@@ -325,6 +327,9 @@ static int kludge_acl_search(struct ldb_module *module, struct ldb_request *req)
 
        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->req = req;
        ac->user_type = what_is_user(module);
@@ -397,6 +402,12 @@ static int kludge_acl_change(struct ldb_module *module, struct ldb_request *req)
 {
        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 SECURITY_SYSTEM:
        case SECURITY_ADMINISTRATOR:
@@ -459,6 +470,8 @@ static int kludge_acl_init(struct ldb_module *module)
        }
 
        data->password_attrs = NULL;
+       data->acl_perform = lp_parm_bool(ldb_get_opaque(ldb, "loadparm"),
+                                        NULL, "acl", "perform", false);
        ldb_module_set_private(module, data);
 
        if (!mem_ctx) {
index 7d696e877e6d6af2d0d49a1a2ceef4149ff9167e..a2d9792a24d1140b0d1d94f519e603913da0aa68 100644 (file)
@@ -416,3 +416,15 @@ const char **dsdb_full_attribute_list(TALLOC_CTX *mem_ctx,
        const char **attr_list = dsdb_full_attribute_list_internal_el(mem_ctx, schema, class_list, query);
        return dedup_attr_list(attr_list);
 }
+
+/* Return the schemaIDGUID of a class */
+
+const struct GUID * class_schemaid_guid_by_lDAPDisplayName(const struct dsdb_schema *schema,
+                                                           const char *name)
+{
+        const struct dsdb_class *object_class = dsdb_class_by_lDAPDisplayName(schema, name);
+        if (!object_class)
+                return NULL;
+
+        return &object_class->schemaIDGUID;
+}
index af6a3d6fb3e7bc965032ce8fef3580cbc5cf173b..543b0f74c5843f94dcee7ab3914ae1c7b1c6221d 100644 (file)
@@ -69,6 +69,21 @@ static uint32_t access_check_max_allowed(const struct security_descriptor *sd,
        return granted & ~denied;
 }
 
+static const struct GUID *get_ace_object_type(struct security_ace *ace)
+{
+        struct GUID *type;
+
+        if (ace->object.object.flags & SEC_ACE_OBJECT_TYPE_PRESENT)
+                type = &ace->object.object.type.type;
+        else if (ace->object.object.flags & SEC_ACE_INHERITED_OBJECT_TYPE_PRESENT)
+                type = &ace->object.object.inherited_type.inherited_type; /* This doesn't look right. Is something wrong with the IDL? */
+        else
+                type = NULL;
+
+        return type;
+
+}
+
 /*
   the main entry point for access checking. 
 */
@@ -153,3 +168,123 @@ done:
 
        return NT_STATUS_OK;
 }
+
+/* modified access check for the purposes of DS security
+ * Lots of code duplication, it will ve united in just one
+ * function eventually */
+
+NTSTATUS sec_access_check_ds(const struct security_descriptor *sd,
+                            const struct security_token *token,
+                            uint32_t access_desired,
+                            uint32_t *access_granted,
+                            struct object_tree *tree)
+{
+        int i;
+        uint32_t bits_remaining;
+        struct object_tree *node;
+        struct GUID *type;
+
+        *access_granted = access_desired;
+        bits_remaining = access_desired;
+
+        /* handle the maximum allowed flag */
+        if (access_desired & SEC_FLAG_MAXIMUM_ALLOWED) {
+                access_desired |= access_check_max_allowed(sd, token);
+                access_desired &= ~SEC_FLAG_MAXIMUM_ALLOWED;
+                *access_granted = access_desired;
+                bits_remaining = access_desired & ~SEC_STD_DELETE;
+        }
+
+        if (access_desired & SEC_FLAG_SYSTEM_SECURITY) {
+                if (security_token_has_privilege(token, SEC_PRIV_SECURITY)) {
+                        bits_remaining &= ~SEC_FLAG_SYSTEM_SECURITY;
+                } else {
+                        return NT_STATUS_PRIVILEGE_NOT_HELD;
+                }
+        }
+
+        /* a NULL dacl allows access */
+        if ((sd->type & SEC_DESC_DACL_PRESENT) && sd->dacl == NULL) {
+               *access_granted = access_desired;
+                return NT_STATUS_OK;
+        }
+
+        /* the owner always gets SEC_STD_WRITE_DAC, SEC_STD_READ_CONTROL and SEC_STD_DELETE */
+        if ((bits_remaining & (SEC_STD_WRITE_DAC|SEC_STD_READ_CONTROL|SEC_STD_DELETE)) &&
+            security_token_has_sid(token, sd->owner_sid)) {
+                bits_remaining &= ~(SEC_STD_WRITE_DAC|SEC_STD_READ_CONTROL|SEC_STD_DELETE);
+        }
+        if ((bits_remaining & SEC_STD_DELETE) &&
+            security_token_has_privilege(token, SEC_PRIV_RESTORE)) {
+                bits_remaining &= ~SEC_STD_DELETE;
+        }
+
+        if (sd->dacl == NULL) {
+                goto done;
+        }
+
+        /* check each ace in turn. */
+        for (i=0; bits_remaining && i < sd->dacl->num_aces; i++) {
+               struct security_ace *ace = &sd->dacl->aces[i];
+
+                if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
+                        continue;
+                }
+
+                if (!security_token_has_sid(token, &ace->trustee)) {
+                        continue;
+                }
+
+                switch (ace->type) {
+                case SEC_ACE_TYPE_ACCESS_ALLOWED:
+                        if (tree)
+                                object_tree_modify_access(tree, ace->access_mask);
+
+                        bits_remaining &= ~ace->access_mask;
+                        break;
+                case SEC_ACE_TYPE_ACCESS_DENIED:
+                        if (bits_remaining & ace->access_mask) {
+                                return NT_STATUS_ACCESS_DENIED;
+                        }
+                        break;
+                case SEC_ACE_TYPE_ACCESS_DENIED_OBJECT:
+                case SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT:
+                        /* check only in case we have provided a tree,
+                         * the ACE has an object type and that type
+                         * is in the tree                           */
+                        type = get_ace_object_type(ace);
+
+                        if (!tree)
+                                continue;
+
+                        if (!type)
+                                node = tree;
+                        else
+                                if (!(node = get_object_tree_by_GUID(tree, type)))
+                                        continue;
+
+                        if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT){
+                                object_tree_modify_access(node, ace->access_mask);
+                        }
+                        else {
+                                if (node->remaining_access & ace->access_mask){
+                                        return NT_STATUS_ACCESS_DENIED;
+                                }
+                        }
+                        break;
+                default:        /* Other ACE types not handled/supported */
+                        break;
+                }
+        }
+
+done:
+        if (bits_remaining != 0) {
+                return NT_STATUS_ACCESS_DENIED;
+        }
+
+        return NT_STATUS_OK;
+}
+
+
+
+
index ca545f817f52dae2fa315d98ba76d209b5d67f89..f1ca20a2e8c9b77448ddbcac7182d1fbf795ea40 100644 (file)
@@ -2,6 +2,7 @@
 PUBLIC_DEPENDENCIES = LIBNDR LIBSECURITY_COMMON
 
 LIBSECURITY_OBJ_FILES = $(addprefix $(libclisrcdir)/security/, \
-                                          security_token.o access_check.o privilege.o sddl.o create_descriptor.o) \
+                                               security_token.o access_check.o privilege.o sddl.o \
+                                               create_descriptor.o object_tree.o)
 
 $(eval $(call proto_header_template,$(libclisrcdir)/security/proto.h,$(LIBSECURITY_OBJ_FILES:.o=.c)))
diff --git a/source4/libcli/security/object_tree.c b/source4/libcli/security/object_tree.c
new file mode 100644 (file)
index 0000000..8a90019
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   security access checking routines
+
+   Copyright (C) Nadezhda Ivanova 2009
+
+   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 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ *  Description: Contains data handler functions for
+ *               the object tree that must be constructed to perform access checks.
+ *               The object tree is an unbalanced tree of depth 3, indexed by
+ *               object type guid. Perhaps a different data structure
+ *               should be concidered later to improve performance
+ *
+ *  Author: Nadezhda Ivanova
+ */
+#include "includes.h"
+#include "libcli/security/security.h"
+#include "lib/util/dlinklist.h"
+#include "librpc/ndr/libndr.h"
+
+/* Adds a new node to the object tree. If attributeSecurityGUID is not zero and
+ * has already been added to the tree, the new node is added as a child of that node
+ * In all other cases as a child of the root
+ */
+
+struct object_tree * insert_in_object_tree(TALLOC_CTX *mem_ctx,
+                                          const struct GUID *schemaGUIDID,
+                                          const struct GUID *attributeSecurityGUID,
+                                          uint32_t init_access,
+                                          struct object_tree *root)
+{
+       struct object_tree * parent = NULL;
+       struct object_tree * new_node;
+
+       new_node = talloc(mem_ctx, struct object_tree);
+       if (!new_node)
+               return NULL;
+       memset(new_node, 0, sizeof(struct object_tree));
+       new_node->remaining_access = init_access;
+
+       if (!root){
+               memcpy(&new_node->guid, schemaGUIDID, sizeof(struct GUID));
+               return new_node;
+       }
+
+       if (attributeSecurityGUID && !GUID_all_zero(attributeSecurityGUID)){
+               parent = get_object_tree_by_GUID(root, attributeSecurityGUID);
+               memcpy(&new_node->guid, attributeSecurityGUID, sizeof(struct GUID));
+       }
+       else
+               memcpy(&new_node->guid, schemaGUIDID, sizeof(struct GUID));
+
+       if (!parent)
+               parent = root;
+
+       new_node->remaining_access = init_access;
+       DLIST_ADD(parent, new_node);
+       return new_node;
+}
+
+/* search by GUID */
+struct object_tree * get_object_tree_by_GUID(struct object_tree *root,
+                                            const struct GUID *guid)
+{
+       struct object_tree *p;
+       struct object_tree *result = NULL;
+
+       if (!root || GUID_equal(&root->guid, guid))
+               result = root;
+       else{
+       for (p = root->children; p != NULL; p = p->next)
+               if ((result = get_object_tree_by_GUID(p, guid)))
+                       break;
+       }
+
+       return result;
+}
+
+/* Change the granted access per each ACE */
+
+void object_tree_modify_access(struct object_tree *root,
+                              uint32_t access)
+{
+       struct object_tree *p;
+       if (root){
+               root->remaining_access &= ~access;
+       }
+
+       for (p = root->children; p != NULL; p = p->next)
+               object_tree_modify_access(p, access);
+}
index 3cfa484816265d4769207226d34aa7519adaa317..18f6c820d1c66abf5c377f8dd60567fd730bb422 100644 (file)
@@ -29,6 +29,15 @@ enum security_user_level {
 
 struct auth_session_info;
 
+struct object_tree {
+       uint32_t remaining_access;
+       struct GUID guid;
+       /* linked list of children */
+       struct object_tree * children;
+       struct object_tree * prev;
+       struct object_tree * next;
+};
+
 /* Moved the dom_sid functions to the top level dir with manual proto header */
 #include "libcli/security/dom_sid.h"
 #include "libcli/security/secace.h"
index 2d3e04eac1bdda1d2fb7d18ac4717599e766ec66..64491c2b18dcff3a81f557dcc3127dfa8ad77681 100644 (file)
@@ -614,6 +614,7 @@ def setup_samdb_partitions(samdb_path, setup_path, message, lp, session_info,
     # - each partition has its own module list then
     modules_list = ["resolve_oids",
                     "rootdse",
+                    "acl",
                     "paged_results",
                     "ranged_results",
                     "anr",