Merge branch 'master' of git://git.samba.org/samba
authorNadezhda Ivanova <nadezhda.ivanova@postpath.com>
Tue, 22 Sep 2009 00:29:28 +0000 (17:29 -0700)
committerNadezhda Ivanova <nadezhda.ivanova@postpath.com>
Tue, 22 Sep 2009 00:29:28 +0000 (17:29 -0700)
16 files changed:
libcli/security/security_descriptor.c
libcli/security/security_descriptor.h
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/lib/ldb/tests/python/sec_descriptor.py
source4/libcli/security/access_check.c
source4/libcli/security/config.mk
source4/libcli/security/create_descriptor.c
source4/libcli/security/object_tree.c [new file with mode: 0644]
source4/libcli/security/security.h
source4/scripting/python/samba/provision.py
source4/selftest/knownfail
source4/setup/provision_configuration_basedn.ldif
source4/setup/provision_schema_basedn.ldif

index dbe11604fd827949eaef6ad5913bb22ee4029142..b77a28185261b9f1e8353a88e6fda93bab31f64d 100644 (file)
@@ -77,6 +77,56 @@ struct security_acl *security_acl_dup(TALLOC_CTX *mem_ctx,
        
 }
 
+struct security_acl *security_acl_concatenate(TALLOC_CTX *mem_ctx,
+                                              const struct security_acl *acl1,
+                                              const struct security_acl *acl2)
+{
+        struct security_acl *nacl;
+        int i;
+
+        if (!acl1 && !acl2)
+                return NULL;
+
+        if (!acl1){
+                nacl = security_acl_dup(mem_ctx, acl2);
+                return nacl;
+        }
+
+        if (!acl2){
+                nacl = security_acl_dup(mem_ctx, acl1);
+                return nacl;
+        }
+
+        nacl = talloc (mem_ctx, struct security_acl);
+        if (nacl == NULL) {
+                return NULL;
+        }
+
+        nacl->revision = acl1->revision;
+        nacl->size = acl1->size + acl2->size;
+        nacl->num_aces = acl1->num_aces + acl2->num_aces;
+
+        if (nacl->num_aces == 0)
+                return nacl;
+
+        nacl->aces = (struct security_ace *)talloc_array (mem_ctx, struct security_ace, acl1->num_aces+acl2->num_aces);
+        if ((nacl->aces == NULL) && (nacl->num_aces > 0)) {
+                goto failed;
+        }
+
+        for (i = 0; i < acl1->num_aces; i++)
+                nacl->aces[i] = acl1->aces[i];
+        for (i = 0; i < acl2->num_aces; i++)
+                nacl->aces[i + acl1->num_aces] = acl2->aces[i];
+
+        return nacl;
+
+ failed:
+        talloc_free (nacl);
+        return NULL;
+
+}
+
 /* 
    talloc and copy a security descriptor
  */
index a377ef59ce2b5133fe7a3cade144e3d0a5b4f878..bc5761ab6fef6a544dca9b826fd13eb6607c2d05 100644 (file)
@@ -64,4 +64,8 @@ struct security_ace *security_ace_create(TALLOC_CTX *mem_ctx,
 struct security_acl *security_acl_dup(TALLOC_CTX *mem_ctx,
                                      const struct security_acl *oacl);
 
+struct security_acl *security_acl_concatenate(TALLOC_CTX *mem_ctx,
+                                              const struct security_acl *acl1,
+                                              const struct security_acl *acl2);
+
 #endif /* __SECURITY_DESCRIPTOR_H__ */
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 01df86e9099aeba922f5147f0442f07a022cf282..155b65f4abd1992e4d9640eb9b0d2fa768035145 100755 (executable)
@@ -249,7 +249,10 @@ userAccountControl: %s""" % userAccountControl
         desc_sddl = desc.as_sddl( self.domain_sid )
         if ace in desc_sddl:
             return
-        desc_sddl = desc_sddl[0:desc_sddl.index("(")] + ace + desc_sddl[desc_sddl.index("("):]
+        if desc_sddl.find("(") >= 0:
+            desc_sddl = desc_sddl[0:desc_sddl.index("(")] + ace + desc_sddl[desc_sddl.index("("):]
+        else:
+            desc_sddl = desc_sddl + ace
         self.modify_desc(object_dn, desc_sddl)
 
     def get_desc_sddl(self, object_dn):
@@ -809,13 +812,11 @@ member: """ + user_dn
         #mod = ""
         self.dacl_add_ace(object_dn, mod)
         desc_sddl = self.get_desc_sddl(object_dn)
-        #print desc_sddl
         # Create additional object into the first one
         object_dn = "OU=test_domain_ou2," + object_dn
         self.delete_force(self.ldb_admin, object_dn)
         self.create_domain_ou(self.ldb_admin, object_dn)
         desc_sddl = self.get_desc_sddl(object_dn)
-        #print desc_sddl
 
     ## Tests for SCHEMA
 
@@ -1397,6 +1398,10 @@ class DaclDescriptorTests(DescriptorTests):
         # Add flag 'protected' in both DACL and SACL so no inherit ACEs
         # can propagate from above
         desc_sddl = desc_sddl.replace(":AI", ":AIP")
+        # colon at the end breaks ldif parsing, fix it
+        res = re.findall(".*?S:", desc_sddl)
+        if res:
+            desc_sddl = desc_sddl.replace("S:", "")
         self.modify_desc(object_dn, desc_sddl)
         # Verify all inheritable ACEs are gone
         desc_sddl = self.get_desc_sddl(object_dn)
@@ -1429,6 +1434,7 @@ class DaclDescriptorTests(DescriptorTests):
         self.create_domain_group(self.ldb_admin, group_dn, sddl)
         # Make sure created group descriptor has NO additional ACEs
         desc_sddl = self.get_desc_sddl(group_dn)
+        print "group descriptor: " + desc_sddl
         self.assertEqual(desc_sddl, sddl)
 
     def test_202(self):
@@ -1590,7 +1596,6 @@ class DaclDescriptorTests(DescriptorTests):
         # Make sure created group object contains only the above inherited ACE(s)
         # that we've added manually
         desc_sddl = self.get_desc_sddl(group_dn)
-        #print desc_sddl
         self.assertTrue("(D;ID;WP;;;AU)" in desc_sddl)
         self.assertTrue("(D;CIIOID;WP;;;CO)" in desc_sddl)
 
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)))
index 6a928273b78cfd6ec559b5e91d7538ea0cb1878b..1054479f288d6a401e774fc2eb7d305e54999e75 100644 (file)
 #include "includes.h"
 #include "libcli/security/security.h"
 
+/* Todos:
+ * build the security token dacl as follows:
+ * SYSTEM: GA, OWNER: GA, LOGIN_SID:GW|GE
+ * Need session id information for the login SID. Probably
+ * the best place for this is during token creation
+ *
+ * Implement SD Invariants
+ * ACE sorting rules
+ * LDAP_SERVER_SD_FLAGS_OID control
+ * ADTS 7.1.3.3 needs to be clarified
+ */
+
 /* the mapping function for generic rights for DS.(GA,GR,GW,GX)
  * The mapping function is passed as an argument to the
  * descriptor calculating routine and depends on the security
@@ -63,6 +75,328 @@ uint32_t map_generic_rights_ds(uint32_t access_mask)
        return access_mask;
 }
 
+/* Not sure what this has to be,
+* and it does not seem to have any influence */
+static bool object_in_list(struct GUID *object_list, struct GUID *object)
+{
+       return true;
+}
+
+
+static bool contains_inheritable_aces(struct security_acl *acl)
+{
+        int i;
+       if (!acl)
+               return false;
+
+       for (i=0; i < acl->num_aces; i++) {
+               struct security_ace *ace = &acl->aces[i];
+               if ((ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) ||
+                   (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT))
+                       return true;
+       }
+
+       return false;
+}
+
+static struct security_acl *preprocess_creator_acl(TALLOC_CTX *mem, struct security_acl *acl)
+{
+       int i;
+       struct security_acl *new_acl = talloc_zero(mem, struct security_acl);
+
+       new_acl->revision = acl->revision;
+       for (i=0; i < acl->num_aces; i++) {
+               struct security_ace *ace = &acl->aces[i];
+               if (!(ace->flags & SEC_ACE_FLAG_INHERITED_ACE)){
+                       new_acl->aces = talloc_realloc(new_acl, new_acl->aces, struct security_ace,
+                                          new_acl->num_aces+1);
+                       if (new_acl->aces == NULL) {
+                               talloc_free(new_acl);
+                               return NULL;
+                       }
+                       new_acl->aces[new_acl->num_aces] = *ace;
+                       /*memcpy(&new_acl->aces[new_acl->num_aces], ace,
+                         sizeof(struct security_ace)); */
+                       new_acl->num_aces++;
+               }
+       }
+       if (new_acl)
+               new_acl->revision = acl->revision;
+       /* Todo what to do if all were inherited and this is empty */
+       return new_acl;
+}
+
+/* This is not exactly as described in the docs. The original seemed to return
+ * only a list of the inherited or flagless ones... */
+
+static bool postprocess_acl(struct security_acl *acl,
+                           struct dom_sid *owner,
+                           struct dom_sid *group,
+                           uint32_t (*generic_map)(uint32_t access_mask))
+{
+       int i;
+       struct dom_sid *co, *cg;
+       TALLOC_CTX *tmp_ctx = talloc_new(acl);
+       if (!generic_map){
+               return false;
+       }
+       co = dom_sid_parse_talloc(tmp_ctx,  SID_CREATOR_OWNER);
+       cg = dom_sid_parse_talloc(tmp_ctx,  SID_CREATOR_GROUP);
+       for (i=0; i < acl->num_aces; i++){
+               struct security_ace *ace = &acl->aces[i];
+               if (!(ace->flags == 0 || ace->flags & SEC_ACE_FLAG_INHERITED_ACE))
+                       continue;
+               if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY)
+                       continue;
+               if (dom_sid_equal(&ace->trustee, co)){
+                       ace->trustee = *owner;
+                       /* perhaps this should be done somewhere else? */
+                       ace->flags &= ~SEC_ACE_FLAG_CONTAINER_INHERIT;
+               }
+               if (dom_sid_equal(&ace->trustee, cg)){
+                       ace->trustee = *group;
+                       ace->flags &= ~SEC_ACE_FLAG_CONTAINER_INHERIT;
+               }
+               ace->access_mask = generic_map(ace->access_mask);
+       }
+
+       talloc_free(tmp_ctx);
+       return true;
+}
+
+static struct security_acl *calculate_inherited_from_parent(TALLOC_CTX *mem_ctx,
+                                               struct security_acl *acl,
+                                               bool is_container,
+                                               struct GUID *object_list)
+{
+       int i;
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       struct security_acl *tmp_acl = talloc_zero(tmp_ctx, struct security_acl);
+       struct security_acl *inh_acl = talloc_zero(tmp_ctx, struct security_acl);
+       struct security_acl *new_acl;
+       struct dom_sid *co, *cg;
+       if (!tmp_acl || !inh_acl)
+               return NULL;
+
+       co = dom_sid_parse_talloc(tmp_ctx,  SID_CREATOR_OWNER);
+       cg = dom_sid_parse_talloc(tmp_ctx,  SID_CREATOR_GROUP);
+
+       for (i=0; i < acl->num_aces; i++){
+               struct security_ace *ace = &acl->aces[i];
+               if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY)
+                       continue;
+
+               if ((ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) ||
+                   (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT)){
+                       tmp_acl->aces = talloc_realloc(tmp_acl, tmp_acl->aces, struct security_ace,
+                                                      tmp_acl->num_aces+1);
+                       if (tmp_acl->aces == NULL) {
+                               talloc_free(tmp_ctx);
+                               return NULL;
+                       }
+
+                       tmp_acl->aces[tmp_acl->num_aces] = *ace;
+                       tmp_acl->aces[tmp_acl->num_aces].flags |= SEC_ACE_FLAG_INHERITED_ACE;
+
+                       if (is_container && (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT))
+                           tmp_acl->aces[tmp_acl->num_aces].flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+
+                       if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+                           ace->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT){
+                               if (!object_in_list(object_list, &ace->object.object.type.type)){
+                                       tmp_acl->aces[tmp_acl->num_aces].flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+                               }
+
+                       }
+                       tmp_acl->num_aces++;
+               }
+       }
+
+       if (is_container){
+               for (i=0; i < acl->num_aces; i++){
+                       struct security_ace *ace = &acl->aces[i];
+
+                       if (ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)
+                               continue;
+                       if (!dom_sid_equal(&ace->trustee, co) && !dom_sid_equal(&ace->trustee, cg))
+                               continue;
+
+                       if ((ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) ||
+                           (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT)){
+                               inh_acl->aces = talloc_realloc(inh_acl, inh_acl->aces, struct security_ace,
+                                                              inh_acl->num_aces+1);
+                               if (inh_acl->aces == NULL){
+                                       talloc_free(tmp_ctx);
+                                       return NULL;
+                               }
+                               inh_acl->aces[inh_acl->num_aces] = *ace;
+                               inh_acl->aces[inh_acl->num_aces].flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+                               inh_acl->aces[inh_acl->num_aces].flags |= SEC_ACE_FLAG_INHERITED_ACE;
+                               inh_acl->num_aces++;
+                       }
+               }
+       }
+       new_acl = security_acl_concatenate(mem_ctx,tmp_acl, inh_acl);
+       if (new_acl)
+               new_acl->revision = acl->revision;
+       talloc_free(tmp_ctx);
+       return new_acl;
+}
+
+/* In the docs this looks == calculate_inherited_from_parent. However,
+ * It shouldn't return the inherited, rather filter them out....
+ */
+static struct security_acl *calculate_inherited_from_creator(TALLOC_CTX *mem_ctx,
+                                               struct security_acl *acl,
+                                               bool is_container,
+                                               struct GUID *object_list)
+{
+       int i;
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+       struct security_acl *tmp_acl = talloc_zero(tmp_ctx, struct security_acl);
+/*     struct security_acl *inh_acl = talloc_zero(tmp_ctx, struct security_acl); */
+       struct security_acl *new_acl;
+       struct dom_sid *co, *cg;
+
+       if (!tmp_acl)
+               return NULL;
+
+       co = dom_sid_parse_talloc(tmp_ctx,  SID_CREATOR_OWNER);
+       cg = dom_sid_parse_talloc(tmp_ctx,  SID_CREATOR_GROUP);
+
+       for (i=0; i < acl->num_aces; i++){
+               struct security_ace *ace = &acl->aces[i];
+               if (ace->flags & SEC_ACE_FLAG_INHERITED_ACE)
+                       continue;
+
+               tmp_acl->aces = talloc_realloc(tmp_acl, tmp_acl->aces, struct security_ace,
+                                             tmp_acl->num_aces+1);
+               tmp_acl->aces[tmp_acl->num_aces] = *ace;
+               tmp_acl->aces[tmp_acl->num_aces].flags  = 0;
+               tmp_acl->num_aces++;
+
+               if (!dom_sid_equal(&ace->trustee, co) && !dom_sid_equal(&ace->trustee, cg))
+                       continue;
+
+               tmp_acl->aces = talloc_realloc(tmp_acl, tmp_acl->aces, struct security_ace,
+                                              tmp_acl->num_aces+1);
+               tmp_acl->aces[tmp_acl->num_aces] = *ace;
+               tmp_acl->aces[tmp_acl->num_aces].flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+               tmp_acl->num_aces++;
+       }
+       new_acl = security_acl_dup(mem_ctx,tmp_acl);
+
+       talloc_free(tmp_ctx);
+       return new_acl;
+}
+
+static bool compute_acl(int acl_type,
+                       struct security_descriptor *parent_sd,
+                       struct security_descriptor *creator_sd,
+                       bool is_container,
+                       uint32_t inherit_flags,
+                       struct GUID *object_list,
+                       uint32_t (*generic_map)(uint32_t access_mask),
+                       struct security_token *token,
+                       struct security_descriptor *new_sd) /* INOUT argument */
+{
+       struct security_acl *p_acl = NULL, *c_acl = NULL, **new_acl;
+       if (acl_type == SEC_DESC_DACL_PRESENT){
+               if (parent_sd)
+                       p_acl = parent_sd->dacl;
+               if (creator_sd)
+                       c_acl = creator_sd->dacl;
+               new_acl = &new_sd->dacl;
+       }
+       else{
+               if (parent_sd)
+                       p_acl = parent_sd->sacl;
+               if (creator_sd)
+                       c_acl = creator_sd->sacl;
+               new_acl = &new_sd->sacl;
+       }
+       if (contains_inheritable_aces(p_acl)){
+               if (!c_acl || (c_acl && inherit_flags & SEC_DEFAULT_DESCRIPTOR)){
+                       *new_acl = calculate_inherited_from_parent(new_sd,
+                                                      p_acl,
+                                                      is_container,
+                                                      object_list);
+                       if (*new_acl == NULL)
+                               goto final;
+                       if (!postprocess_acl(*new_acl, new_sd->owner_sid,
+                                            new_sd->group_sid, generic_map))
+                               return false;
+                       else
+                               goto final;
+               }
+               if (c_acl && !(inherit_flags & SEC_DEFAULT_DESCRIPTOR)){
+                       struct security_acl *pr_acl, *tmp_acl, *tpr_acl;
+                       tpr_acl = preprocess_creator_acl(new_sd, c_acl);
+                       tmp_acl = calculate_inherited_from_creator(new_sd,
+                                                     tpr_acl,
+                                                     is_container,
+                                                     object_list);
+                       /* Todo some refactoring here! */
+                       if (acl_type == SEC_DESC_DACL_PRESENT &&
+                           !(creator_sd->type & SECINFO_PROTECTED_DACL) &&
+                           (inherit_flags & SEC_DACL_AUTO_INHERIT)){
+                               pr_acl = calculate_inherited_from_parent(new_sd,
+                                                            p_acl,
+                                                            is_container,
+                                                            object_list);
+
+                               *new_acl = security_acl_concatenate(new_sd, tmp_acl, pr_acl);
+                               new_sd->type |= SEC_DESC_DACL_AUTO_INHERITED;
+                       }
+                       else if (acl_type == SEC_DESC_SACL_PRESENT &&
+                           !(creator_sd->type & SECINFO_PROTECTED_SACL) &&
+                           (inherit_flags & SEC_SACL_AUTO_INHERIT)){
+                               pr_acl = calculate_inherited_from_parent(new_sd,
+                                                            p_acl,
+                                                            is_container,
+                                                            object_list);
+
+                               *new_acl = security_acl_concatenate(new_sd, tmp_acl, pr_acl);
+                               new_sd->type |= SEC_DESC_SACL_AUTO_INHERITED;
+                       }
+               }
+               if (*new_acl == NULL)
+                       goto final;
+               if (!postprocess_acl(*new_acl, new_sd->owner_sid,
+                                    new_sd->group_sid,generic_map))
+                       return false;
+               else
+                       goto final;
+       }
+       else{
+               if (!c_acl){
+                       if (acl_type == SEC_DESC_DACL_PRESENT && token->default_dacl)
+                               *new_acl = security_acl_dup(new_sd, token->default_dacl);
+               }
+               else{
+                       *new_acl = preprocess_creator_acl(new_sd,c_acl);
+                       if (*new_acl == NULL)
+                               goto final;
+                       if (!postprocess_acl(*new_acl, new_sd->owner_sid,
+                                            new_sd->group_sid,generic_map))
+                               return false;
+                       else
+                               goto final;
+               }
+       }
+final:
+       if (acl_type == SEC_DESC_DACL_PRESENT && new_sd->dacl)
+               new_sd->type |= SEC_DESC_DACL_PRESENT;
+
+       if (acl_type == SEC_DESC_SACL_PRESENT && new_sd->sacl)
+               new_sd->type |= SEC_DESC_SACL_PRESENT;
+       /* This is a hack to handle the fact that
+        * apprantly any AI flag provided by the user is preserved */
+       if (creator_sd)
+               new_sd->type |= creator_sd->type;
+       return true;
+}
+
 struct security_descriptor *create_security_descriptor(TALLOC_CTX *mem_ctx,
                                                       struct security_descriptor *parent_sd,
                                                       struct security_descriptor *creator_sd,
@@ -108,10 +442,20 @@ struct security_descriptor *create_security_descriptor(TALLOC_CTX *mem_ctx,
                talloc_free(new_sd);
                return NULL;
        }
-       /* Todo remove */
-       if (creator_sd && creator_sd->type & SEC_DESC_DACL_PRESENT){
-               new_sd->dacl = security_acl_dup(new_sd, creator_sd->dacl);
-               new_sd->type |= SEC_DESC_DACL_PRESENT;
+
+       if (!compute_acl(SEC_DESC_DACL_PRESENT, parent_sd, creator_sd,
+                        is_container, inherit_flags, object_list,
+                        generic_map,token,new_sd)){
+               talloc_free(new_sd);
+               return NULL;
        }
+
+       if (!compute_acl(SEC_DESC_SACL_PRESENT, parent_sd, creator_sd,
+                        is_container, inherit_flags, object_list,
+                        generic_map, token,new_sd)){
+               talloc_free(new_sd);
+               return NULL;
+       }
+
        return new_sd;
 }
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 9a41709830132f05c0ffacb36e63749c00257e1d..64491c2b18dcff3a81f557dcc3127dfa8ad77681 100644 (file)
@@ -73,6 +73,39 @@ def find_setup_dir():
         return ret
     raise Exception("Unable to find setup directory.")
 
+def get_schema_descriptor(domain_sid):
+    sddl = "O:SAG:SAD:(A;CI;RPLCLORC;;;AU)(A;CI;RPWPCRCCLCLORCWOWDSW;;;SA)" \
+           "(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)" \
+           "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
+           "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
+           "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
+           "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
+           "S:(AU;SA;WPCCDCWOWDSDDTSW;;;WD)" \
+           "(AU;CISA;WP;;;WD)(AU;SA;CR;;;BA)" \
+           "(AU;SA;CR;;;DU)(OU;SA;CR;e12b56b6-0a95-11d1-adbb-00c04fd8d5cd;;WD)" \
+           "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
+    sec = security.descriptor.from_sddl(sddl, domain_sid)
+    return b64encode(ndr_pack(sec))
+
+def get_config_descriptor(domain_sid):
+    sddl = "O:EAG:EAD:(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
+           "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
+           "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
+           "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
+           "(OA;;CR;1131f6ab-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
+           "(OA;;CR;1131f6ac-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
+           "(A;;RPLCLORC;;;AU)(A;CI;RPWPCRCCDCLCLORCWOWDSDDTSW;;;EA)" \
+           "(A;;RPWPCRCCDCLCLORCWOWDSDDTSW;;;SY)(A;CIIO;RPWPCRCCLCLORCWOWDSDSW;;;DA)" \
+           "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;ED)" \
+           "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;ED)" \
+           "(OA;;CR;1131f6ad-9c07-11d1-f79f-00c04fc2dcd2;;BA)" \
+           "(OA;;CR;89e95b76-444d-4c62-991a-0facbeda640c;;BA)" \
+           "(OA;;CR;1131f6aa-9c07-11d1-f79f-00c04fc2dcd2;;S-1-5-21-3191434175-1265308384-3577286990-498)" \
+           "S:(AU;SA;WPWOWD;;;WD)(AU;SA;CR;;;BA)(AU;SA;CR;;;DU)" \
+           "(OU;SA;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;WD)"
+    sec = security.descriptor.from_sddl(sddl, domain_sid)
+    return b64encode(ndr_pack(sec))
+
 
 DEFAULTSITE = "Default-First-Site-Name"
 
@@ -144,7 +177,7 @@ class ProvisionResult(object):
         self.samdb = None
         
 class Schema(object):
-    def __init__(self, setup_path, schemadn=None, 
+    def __init__(self, setup_path, domain_sid, schemadn=None,
                  serverdn=None, sambadn=None, ldap_backend_type=None):
         """Load schema for the SamDB from the AD schema files and samba4_schema.ldif
         
@@ -167,8 +200,11 @@ class Schema(object):
                                                   {"SCHEMADN": schemadn,
                                                    "SERVERDN": serverdn,
                                                    })
+
+        descr = get_schema_descriptor(domain_sid)
         self.schema_dn_add = read_and_sub_file(setup_path("provision_schema_basedn.ldif"),
-                                               {"SCHEMADN": schemadn
+                                               {"SCHEMADN": schemadn,
+                                                "DESCRIPTOR": descr
                                                 })
 
         prefixmap = open(setup_path("prefixMap.txt"), 'r').read()
@@ -578,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",
@@ -907,7 +944,7 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
                            ldap_backend=ldap_backend, serverrole=serverrole)
 
     if (schema == None):
-        schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn,
+        schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
             sambadn=names.sambadn, ldap_backend_type=ldap_backend.ldap_backend_type)
 
     # Load the database, but importantly, use Ldb not SamDB as we don't want to load the global schema
@@ -988,8 +1025,10 @@ def setup_samdb(path, setup_path, session_info, credentials, lp,
             })
 
         message("Adding configuration container")
+        descr = get_config_descriptor(domainsid);
         setup_add_ldif(samdb, setup_path("provision_configuration_basedn.ldif"), {
             "CONFIGDN": names.configdn, 
+            "DESCRIPTOR": descr,
             })
         message("Modifying configuration container")
         setup_modify_ldif(samdb, setup_path("provision_configuration_basedn_modify.ldif"), {
@@ -1109,7 +1148,7 @@ def provision(setup_dir, message, session_info,
     """
 
     def setup_path(file):
-        return os.path.join(setup_dir, file)
+      return os.path.join(setup_dir, file)
 
     if domainsid is None:
       domainsid = security.random_sid()
@@ -1192,7 +1231,7 @@ def provision(setup_dir, message, session_info,
 
     ldapi_url = "ldapi://%s" % urllib.quote(paths.s4_ldapi_path, safe="")
     
-    schema = Schema(setup_path, schemadn=names.schemadn, serverdn=names.serverdn,
+    schema = Schema(setup_path, domainsid, schemadn=names.schemadn, serverdn=names.serverdn,
         sambadn=names.sambadn, ldap_backend_type=ldap_backend_type)
     
     secrets_credentials = credentials
index d4ad716d6ae691a0b08283d547f4a245b3e1bade..fcb16c98145a8404afb030403919dc3100f9e5c3 100644 (file)
@@ -56,4 +56,5 @@ samba4.winbind.struct.*.LOOKUP_NAME_SID   # Not yet working in winbind
 ^samba4.*base.delaywrite.*update of write time using SET_END_OF_FILE$
 ^samba4.*base.delaywrite.*update of write time using SET_ALLOCATION_SIZE$
 ^samba4.ldap.python \(dc\).Test add_ldif\(\) with BASE64 security descriptor input using WRONG domain SID$
-^samba4.ldap.secdesc.python
\ No newline at end of file
+^samba4.ldap.python \(dc\).Testing ldb.add_ldif\(\) for nTSecurityDescriptor
+^samba4.ldap.secdesc.python
index f009113f10e38da131bfcf863eea539d3e14a56c..b385359a61276bb1477293a0cca5f99e29046868 100644 (file)
@@ -5,3 +5,4 @@ dn: ${CONFIGDN}
 objectClass: top
 objectClass: configuration
 cn: Configuration
+nTSecurityDescriptor:: ${DESCRIPTOR}
index 8c7ce88bac9a21261c356f5baf7641600377bb9f..5301a11965a23b07de41af5553c26dffed461e42 100644 (file)
@@ -5,3 +5,4 @@ dn: ${SCHEMADN}
 objectClass: top
 objectClass: dMD
 cn: Schema
+nTSecurityDescriptor:: ${DESCRIPTOR}