diff options
Diffstat (limited to 'source4')
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/acl_read.c | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/acl_read.c b/source4/dsdb/samdb/ldb_modules/acl_read.c index 9607ed05ee7..280845a47a5 100644 --- a/source4/dsdb/samdb/ldb_modules/acl_read.c +++ b/source4/dsdb/samdb/ldb_modules/acl_read.c @@ -38,6 +38,8 @@ #include "param/param.h" #include "dsdb/samdb/ldb_modules/util.h" +/* The attributeSecurityGuid for the Public Information Property-Set */ +#define PUBLIC_INFO_PROPERTY_SET "e48d0154-bcf8-11d1-8702-00c04fb96050" struct aclread_context { struct ldb_module *module; @@ -262,6 +264,219 @@ static uint32_t get_attr_access_mask(const struct dsdb_attribute *attr, return access_mask; } +/* helper struct for traversing the attributes in the search-tree */ +struct parse_tree_aclread_ctx { + struct aclread_context *ac; + TALLOC_CTX *mem_ctx; + struct dom_sid *sid; + struct ldb_dn *dn; + struct security_descriptor *sd; + const struct dsdb_class *objectclass; + bool suppress_result; +}; + +/* + * Checks that the user has sufficient access rights to view an attribute + */ +static int check_attr_access_rights(TALLOC_CTX *mem_ctx, const char *attr_name, + struct aclread_context *ac, + struct security_descriptor *sd, + const struct dsdb_class *objectclass, + struct dom_sid *sid, struct ldb_dn *dn, + bool *is_public_info) +{ + int ret; + const struct dsdb_attribute *attr = NULL; + uint32_t access_mask; + struct ldb_context *ldb = ldb_module_get_ctx(ac->module); + + *is_public_info = false; + + attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, attr_name); + if (!attr) { + ldb_debug_set(ldb, + LDB_DEBUG_TRACE, + "acl_read: %s cannot find attr[%s] in schema," + "ignoring\n", + ldb_dn_get_linearized(dn), attr_name); + return LDB_SUCCESS; + } + + /* + * If we have no Read Property (RP) rights for a child object, it should + * still appear as a visible object in 'objectClass=*' searches, + * as long as we have List Contents (LC) rights for it. + * This is needed for the acl.py tests (e.g. test_search1()). + * I couldn't find the Windows behaviour documented in the specs, so + * this is a guess, but it seems to only apply to attributes in the + * Public Information Property Set that have the systemOnly flag set to + * TRUE. (This makes sense in a way, as it's not disclosive to find out + * that a child object has a 'objectClass' or 'name' attribute, as every + * object has these attributes). + */ + if (attr->systemOnly) { + struct GUID public_info_guid; + NTSTATUS status; + + status = GUID_from_string(PUBLIC_INFO_PROPERTY_SET, + &public_info_guid); + if (!NT_STATUS_IS_OK(status)) { + ldb_set_errstring(ldb, "Public Info GUID parse error"); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (GUID_compare(&attr->attributeSecurityGUID, + &public_info_guid) == 0) { + *is_public_info = true; + } + } + + access_mask = get_attr_access_mask(attr, ac->sd_flags); + + /* the access-mask should be non-zero. Skip attribute otherwise */ + if (access_mask == 0) { + DBG_ERR("Could not determine access mask for attribute %s\n", + attr_name); + return LDB_SUCCESS; + } + + ret = acl_check_access_on_attribute(ac->module, mem_ctx, sd, sid, + access_mask, attr, objectclass); + + if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) { + return ret; + } + + if (ret != LDB_SUCCESS) { + ldb_debug_set(ldb, LDB_DEBUG_FATAL, + "acl_read: %s check attr[%s] gives %s - %s\n", + ldb_dn_get_linearized(dn), attr_name, + ldb_strerror(ret), ldb_errstring(ldb)); + return ret; + } + + return LDB_SUCCESS; +} + +/* + * Returns the attribute name for this particular level of a search operation + * parse-tree. + */ +static const char * parse_tree_get_attr(struct ldb_parse_tree *tree) +{ + const char *attr = NULL; + + switch (tree->operation) { + case LDB_OP_EQUALITY: + case LDB_OP_GREATER: + case LDB_OP_LESS: + case LDB_OP_APPROX: + attr = tree->u.equality.attr; + break; + case LDB_OP_SUBSTRING: + attr = tree->u.substring.attr; + break; + case LDB_OP_PRESENT: + attr = tree->u.present.attr; + break; + case LDB_OP_EXTENDED: + attr = tree->u.extended.attr; + break; + + /* we'll check LDB_OP_AND/_OR/_NOT children later on in the walk */ + default: + break; + } + return attr; +} + +/* + * Checks a single attribute in the search parse-tree to make sure the user has + * sufficient rights to view it. + */ +static int parse_tree_check_attr_access(struct ldb_parse_tree *tree, + void *private_context) +{ + struct parse_tree_aclread_ctx *ctx = NULL; + const char *attr_name = NULL; + bool is_public_info = false; + int ret; + + ctx = (struct parse_tree_aclread_ctx *)private_context; + + /* + * we can skip any further checking if we already know that this object + * shouldn't be visible in this user's search + */ + if (ctx->suppress_result) { + return LDB_SUCCESS; + } + + /* skip this level of the search-tree if it has no attribute to check */ + attr_name = parse_tree_get_attr(tree); + if (attr_name == NULL) { + return LDB_SUCCESS; + } + + ret = check_attr_access_rights(ctx->mem_ctx, attr_name, ctx->ac, + ctx->sd, ctx->objectclass, ctx->sid, + ctx->dn, &is_public_info); + + /* + * if the user does not have the rights to view this attribute, then we + * should not return the object as a search result, i.e. act as if the + * object doesn't exist (for this particular user, at least) + */ + if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) { + + /* + * We make an exception for attribute=* searches involving the + * Public Information property-set. This allows searches like + * objectClass=* to return visible objects, even if the user + * doesn't have Read Property rights on the attribute + */ + if (tree->operation == LDB_OP_PRESENT && is_public_info) { + return LDB_SUCCESS; + } + + ctx->suppress_result = true; + return LDB_SUCCESS; + } + + return ret; +} + +/* + * Traverse the search-tree to check that the user has sufficient access rights + * to view all the attributes. + */ +static int check_search_ops_access(struct aclread_context *ac, + TALLOC_CTX *mem_ctx, + struct security_descriptor *sd, + const struct dsdb_class *objectclass, + struct dom_sid *sid, struct ldb_dn *dn, + bool *suppress_result) +{ + int ret; + struct parse_tree_aclread_ctx ctx = { 0 }; + struct ldb_parse_tree *tree = ac->req->op.search.tree; + + ctx.ac = ac; + ctx.mem_ctx = mem_ctx; + ctx.suppress_result = false; + ctx.sid = sid; + ctx.dn = dn; + ctx.sd = sd; + ctx.objectclass = objectclass; + + /* walk the search tree, checking each attribute as we go */ + ret = ldb_parse_tree_walk(tree, parse_tree_check_attr_access, &ctx); + + /* return whether this search result should be hidden to this user */ + *suppress_result = ctx.suppress_result; + return ret; +} + static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) { struct ldb_context *ldb; @@ -275,6 +490,7 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) TALLOC_CTX *tmp_ctx; uint32_t instanceType; const struct dsdb_class *objectclass; + bool suppress_result = false; ac = talloc_get_type(req->context, struct aclread_context); ldb = ldb_module_get_ctx(ac->module); @@ -436,6 +652,37 @@ static int aclread_callback(struct ldb_request *req, struct ldb_reply *ares) goto fail; } } + + /* + * check access rights for the search attributes, as well as the + * attribute values actually being returned + */ + ret = check_search_ops_access(ac, tmp_ctx, sd, objectclass, sid, + msg->dn, &suppress_result); + if (ret != LDB_SUCCESS) { + ldb_debug_set(ldb, LDB_DEBUG_FATAL, + "acl_read: %s check search ops %s - %s\n", + ldb_dn_get_linearized(msg->dn), + ldb_strerror(ret), ldb_errstring(ldb)); + goto fail; + } + + if (suppress_result) { + + /* + * As per the above logic, we strip replPropertyMetaData + * out of the msg so that the dirysync module will do + * what is needed (return just the objectGUID if it's, + * deleted, or remove the object if it is not). + */ + if (ac->indirsync) { + ldb_msg_remove_attr(msg, "replPropertyMetaData"); + } else { + talloc_free(tmp_ctx); + return LDB_SUCCESS; + } + } + for (i=0; i < msg->num_elements; i++) { if (!aclread_is_inaccessible(&msg->elements[i])) { num_of_attrs++; |