summaryrefslogtreecommitdiff
path: root/source4
diff options
context:
space:
mode:
Diffstat (limited to 'source4')
-rw-r--r--source4/dsdb/samdb/ldb_modules/acl.c1151
-rw-r--r--source4/dsdb/samdb/ldb_modules/config.mk12
-rw-r--r--source4/dsdb/samdb/ldb_modules/kludge_acl.c13
-rw-r--r--source4/dsdb/schema/schema_query.c12
-rwxr-xr-xsource4/lib/ldb/tests/python/sec_descriptor.py13
-rw-r--r--source4/libcli/security/access_check.c135
-rw-r--r--source4/libcli/security/config.mk3
-rw-r--r--source4/libcli/security/create_descriptor.c352
-rw-r--r--source4/libcli/security/object_tree.c106
-rw-r--r--source4/libcli/security/security.h9
-rw-r--r--source4/scripting/python/samba/provision.py49
-rw-r--r--source4/selftest/knownfail3
-rw-r--r--source4/setup/provision_configuration_basedn.ldif1
-rw-r--r--source4/setup/provision_schema_basedn.ldif1
14 files changed, 1845 insertions, 15 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/acl.c b/source4/dsdb/samdb/ldb_modules/acl.c
new file mode 100644
index 0000000000..1b02abcb8e
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/acl.c
@@ -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
+};
diff --git a/source4/dsdb/samdb/ldb_modules/config.mk b/source4/dsdb/samdb/ldb_modules/config.mk
index 9384d062a4..a49b238591 100644
--- a/source4/dsdb/samdb/ldb_modules/config.mk
+++ b/source4/dsdb/samdb/ldb_modules/config.mk
@@ -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
diff --git a/source4/dsdb/samdb/ldb_modules/kludge_acl.c b/source4/dsdb/samdb/ldb_modules/kludge_acl.c
index 15db491171..34f848de8a 100644
--- a/source4/dsdb/samdb/ldb_modules/kludge_acl.c
+++ b/source4/dsdb/samdb/ldb_modules/kludge_acl.c
@@ -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) {
diff --git a/source4/dsdb/schema/schema_query.c b/source4/dsdb/schema/schema_query.c
index 7d696e877e..a2d9792a24 100644
--- a/source4/dsdb/schema/schema_query.c
+++ b/source4/dsdb/schema/schema_query.c
@@ -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;
+}
diff --git a/source4/lib/ldb/tests/python/sec_descriptor.py b/source4/lib/ldb/tests/python/sec_descriptor.py
index 01df86e909..155b65f4ab 100755
--- a/source4/lib/ldb/tests/python/sec_descriptor.py
+++ b/source4/lib/ldb/tests/python/sec_descriptor.py
@@ -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)
diff --git a/source4/libcli/security/access_check.c b/source4/libcli/security/access_check.c
index af6a3d6fb3..543b0f74c5 100644
--- a/source4/libcli/security/access_check.c
+++ b/source4/libcli/security/access_check.c
@@ -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;
+}
+
+
+
+
diff --git a/source4/libcli/security/config.mk b/source4/libcli/security/config.mk
index ca545f817f..f1ca20a2e8 100644
--- a/source4/libcli/security/config.mk
+++ b/source4/libcli/security/config.mk
@@ -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/create_descriptor.c b/source4/libcli/security/create_descriptor.c
index 6a928273b7..1054479f28 100644
--- a/source4/libcli/security/create_descriptor.c
+++ b/source4/libcli/security/create_descriptor.c
@@ -29,6 +29,18 @@
#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
index 0000000000..8a90019a59
--- /dev/null
+++ b/source4/libcli/security/object_tree.c
@@ -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);
+}
diff --git a/source4/libcli/security/security.h b/source4/libcli/security/security.h
index 3cfa484816..18f6c820d1 100644
--- a/source4/libcli/security/security.h
+++ b/source4/libcli/security/security.h
@@ -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"
diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py
index 9a41709830..64491c2b18 100644
--- a/source4/scripting/python/samba/provision.py
+++ b/source4/scripting/python/samba/provision.py
@@ -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
diff --git a/source4/selftest/knownfail b/source4/selftest/knownfail
index d4ad716d6a..fcb16c9814 100644
--- a/source4/selftest/knownfail
+++ b/source4/selftest/knownfail
@@ -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
diff --git a/source4/setup/provision_configuration_basedn.ldif b/source4/setup/provision_configuration_basedn.ldif
index f009113f10..b385359a61 100644
--- a/source4/setup/provision_configuration_basedn.ldif
+++ b/source4/setup/provision_configuration_basedn.ldif
@@ -5,3 +5,4 @@ dn: ${CONFIGDN}
objectClass: top
objectClass: configuration
cn: Configuration
+nTSecurityDescriptor:: ${DESCRIPTOR}
diff --git a/source4/setup/provision_schema_basedn.ldif b/source4/setup/provision_schema_basedn.ldif
index 8c7ce88bac..5301a11965 100644
--- a/source4/setup/provision_schema_basedn.ldif
+++ b/source4/setup/provision_schema_basedn.ldif
@@ -5,3 +5,4 @@ dn: ${SCHEMADN}
objectClass: top
objectClass: dMD
cn: Schema
+nTSecurityDescriptor:: ${DESCRIPTOR}