--- /dev/null
+/*
+ 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
+};
#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
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,
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;
}