/* return an array of SIDs from a ldb_message given an attribute name assumes the SIDs are in extended DN format */ WERROR samdb_result_sid_array_dn(struct ldb_context *sam_ctx, struct ldb_message *msg, TALLOC_CTX *mem_ctx, const char *attr, const struct dom_sid ***sids) { struct ldb_message_element *el; unsigned int i; el = ldb_msg_find_element(msg, attr); if (!el) { *sids = NULL; return WERR_OK; } (*sids) = talloc_array(mem_ctx, const struct dom_sid *, el->num_values + 1); W_ERROR_HAVE_NO_MEMORY(*sids); for (i=0; i<el->num_values; i++) { struct ldb_dn *dn = ldb_dn_from_ldb_val(mem_ctx, sam_ctx, &el->values[i]); NTSTATUS status; struct dom_sid *sid; sid = talloc(*sids, struct dom_sid); W_ERROR_HAVE_NO_MEMORY(sid); status = dsdb_get_extended_dn_sid(dn, sid, "SID"); if (!NT_STATUS_IS_OK(status)) { return WERR_INTERNAL_DB_CORRUPTION; } (*sids)[i] = sid; } (*sids)[i] = NULL; return WERR_OK; }
/* * This function generates the transitive closure of a given SAM object "dn_val" * (it basically expands nested memberships). * If the object isn't located in the "res_sids" structure yet and the * "only_childs" flag is false, we add it to "res_sids". * Then we've always to consider the "memberOf" attributes. We invoke the * function recursively on each of it with the "only_childs" flag set to * "false". * The "only_childs" flag is particularly useful if you have a user object and * want to include all it's groups (referenced with "memberOf") but not itself * or considering if that object matches the filter. * * At the beginning "res_sids" should reference to a NULL pointer. */ NTSTATUS dsdb_expand_nested_groups(struct ldb_context *sam_ctx, struct ldb_val *dn_val, const bool only_childs, const char *filter, TALLOC_CTX *res_sids_ctx, struct dom_sid **res_sids, unsigned int *num_res_sids) { const char * const attrs[] = { "memberOf", NULL }; unsigned int i; int ret; bool already_there; struct ldb_dn *dn; struct dom_sid sid; TALLOC_CTX *tmp_ctx; struct ldb_result *res; NTSTATUS status; const struct ldb_message_element *el; if (*res_sids == NULL) { *num_res_sids = 0; } if (!sam_ctx) { DEBUG(0, ("No SAM available, cannot determine local groups\n")); return NT_STATUS_INVALID_SYSTEM_SERVICE; } tmp_ctx = talloc_new(res_sids_ctx); dn = ldb_dn_from_ldb_val(tmp_ctx, sam_ctx, dn_val); if (dn == NULL) { talloc_free(tmp_ctx); DEBUG(0, (__location__ ": we failed parsing DN %.*s, so we cannot calculate the group token\n", (int)dn_val->length, dn_val->data)); return NT_STATUS_INTERNAL_DB_CORRUPTION; } status = dsdb_get_extended_dn_sid(dn, &sid, "SID"); if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { /* If we fail finding a SID then this is no error since it could * be a non SAM object - e.g. a group with object class * "groupOfNames" */ talloc_free(tmp_ctx); return NT_STATUS_OK; } else if (!NT_STATUS_IS_OK(status)) { DEBUG(0, (__location__ ": when parsing DN '%s' we failed to parse it's SID component, so we cannot calculate the group token: %s\n", ldb_dn_get_extended_linearized(tmp_ctx, dn, 1), nt_errstr(status))); talloc_free(tmp_ctx); return status; } if (!ldb_dn_minimise(dn)) { talloc_free(tmp_ctx); return NT_STATUS_INTERNAL_DB_CORRUPTION; } if (only_childs) { ret = dsdb_search_dn(sam_ctx, tmp_ctx, &res, dn, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN); } else { /* This is an O(n^2) linear search */ already_there = sids_contains_sid(*res_sids, *num_res_sids, &sid); if (already_there) { talloc_free(tmp_ctx); return NT_STATUS_OK; } ret = dsdb_search(sam_ctx, tmp_ctx, &res, dn, LDB_SCOPE_BASE, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN, "%s", filter); } if (ret == LDB_ERR_NO_SUCH_OBJECT) { talloc_free(tmp_ctx); return NT_STATUS_OK; } if (ret != LDB_SUCCESS) { DEBUG(1, (__location__ ": dsdb_search for %s failed: %s\n", ldb_dn_get_extended_linearized(tmp_ctx, dn, 1), ldb_errstring(sam_ctx))); talloc_free(tmp_ctx); return NT_STATUS_INTERNAL_DB_CORRUPTION; } /* We may get back 0 results, if the SID didn't match the filter - such as it wasn't a domain group, for example */ if (res->count != 1) { talloc_free(tmp_ctx); return NT_STATUS_OK; } /* We only apply this test once we know the SID matches the filter */ if (!only_childs) { *res_sids = talloc_realloc(res_sids_ctx, *res_sids, struct dom_sid, *num_res_sids + 1); NT_STATUS_HAVE_NO_MEMORY_AND_FREE(*res_sids, tmp_ctx); (*res_sids)[*num_res_sids] = sid; ++(*num_res_sids); }
/* Return the members of this group (which may be a domain group or an alias) */ NTSTATUS dsdb_enum_group_mem(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, struct ldb_dn *dn, struct dom_sid **members_out, unsigned int *pnum_members) { struct ldb_message *msg; unsigned int i, j; int ret; struct dom_sid *members; struct ldb_message_element *member_el; const char *attrs[] = { "member", NULL }; NTSTATUS status; TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); NT_STATUS_HAVE_NO_MEMORY(tmp_ctx); ret = dsdb_search_one(ldb, tmp_ctx, &msg, dn, LDB_SCOPE_BASE, attrs, DSDB_SEARCH_SHOW_EXTENDED_DN, NULL); if (ret == LDB_ERR_NO_SUCH_OBJECT) { talloc_free(tmp_ctx); return NT_STATUS_OBJECT_NAME_NOT_FOUND; } if (ret != LDB_SUCCESS) { DEBUG(1, ("dsdb_enum_group_mem: dsdb_search for %s failed: %s\n", ldb_dn_get_linearized(dn), ldb_errstring(ldb))); return NT_STATUS_INTERNAL_DB_CORRUPTION; } member_el = ldb_msg_find_element(msg, "member"); if (!member_el) { *members_out = NULL; *pnum_members = 0; talloc_free(tmp_ctx); return NT_STATUS_OK; } members = talloc_array(mem_ctx, struct dom_sid, member_el->num_values); if (members == NULL) { return NT_STATUS_NO_MEMORY; } j = 0; for (i=0; i <member_el->num_values; i++) { struct ldb_dn *member_dn = ldb_dn_from_ldb_val(tmp_ctx, ldb, &member_el->values[i]); if (!member_dn || !ldb_dn_validate(member_dn)) { DEBUG(1, ("Could not parse %*.*s as a DN\n", (int)member_el->values[i].length, (int)member_el->values[i].length, (const char *)member_el->values[i].data)); talloc_free(tmp_ctx); return NT_STATUS_INTERNAL_DB_CORRUPTION; } status = dsdb_get_extended_dn_sid(member_dn, &members[j], "SID"); if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { /* If we fail finding a SID then this is no error since * it could be a non SAM object - e.g. a contact */ continue; } else if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("When parsing DN '%s' we failed to parse it's SID component, so we cannot fetch the membership: %s\n", ldb_dn_get_extended_linearized(tmp_ctx, member_dn, 1), nt_errstr(status))); talloc_free(tmp_ctx); return status; } ++j; } *members_out = talloc_steal(mem_ctx, members); *pnum_members = j; talloc_free(tmp_ctx); return NT_STATUS_OK; }