s3: in sys_popen(), add a debug message for failed extract_args()
[Samba.git] / source4 / dsdb / common / util_groups.c
blobb5aecbafe916fe81f5659e7c64dba04a77715cb3
1 /*
2 Unix SMB/CIFS implementation.
3 Password and authentication handling
4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2001-2010
5 Copyright (C) Stefan Metzmacher 2005
6 Copyright (C) Matthias Dieter Wallnöfer 2009
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "auth/auth.h"
24 #include <ldb.h>
25 #include "dsdb/samdb/samdb.h"
26 #include "libcli/security/security.h"
27 #include "dsdb/common/util.h"
29 /* This function tests if a SID structure "sids" contains the SID "sid" */
30 static bool sids_contains_sid(const struct dom_sid *sids,
31 const unsigned int num_sids,
32 const struct dom_sid *sid)
34 unsigned int i;
36 for (i = 0; i < num_sids; i++) {
37 if (dom_sid_equal(&sids[i], sid))
38 return true;
40 return false;
44 * This function generates the transitive closure of a given SAM object "dn_val"
45 * (it basically expands nested memberships).
46 * If the object isn't located in the "res_sids" structure yet and the
47 * "only_childs" flag is false, we add it to "res_sids".
48 * Then we've always to consider the "memberOf" attributes. We invoke the
49 * function recursively on each of it with the "only_childs" flag set to
50 * "false".
51 * The "only_childs" flag is particularly useful if you have a user object and
52 * want to include all it's groups (referenced with "memberOf") but not itself
53 * or considering if that object matches the filter.
55 * At the beginning "res_sids" should reference to a NULL pointer.
57 NTSTATUS dsdb_expand_nested_groups(struct ldb_context *sam_ctx,
58 struct ldb_val *dn_val, const bool only_childs, const char *filter,
59 TALLOC_CTX *res_sids_ctx, struct dom_sid **res_sids,
60 unsigned int *num_res_sids)
62 const char * const attrs[] = { "memberOf", NULL };
63 unsigned int i;
64 int ret;
65 bool already_there;
66 struct ldb_dn *dn;
67 struct dom_sid sid;
68 TALLOC_CTX *tmp_ctx;
69 struct ldb_result *res;
70 NTSTATUS status;
71 const struct ldb_message_element *el;
73 if (*res_sids == NULL) {
74 *num_res_sids = 0;
77 if (!sam_ctx) {
78 DEBUG(0, ("No SAM available, cannot determine local groups\n"));
79 return NT_STATUS_INVALID_SYSTEM_SERVICE;
82 tmp_ctx = talloc_new(res_sids_ctx);
84 dn = ldb_dn_from_ldb_val(tmp_ctx, sam_ctx, dn_val);
85 if (dn == NULL) {
86 talloc_free(tmp_ctx);
87 DEBUG(0, (__location__ ": we failed parsing DN %.*s, so we cannot calculate the group token\n",
88 (int)dn_val->length, dn_val->data));
89 return NT_STATUS_INTERNAL_DB_CORRUPTION;
92 status = dsdb_get_extended_dn_sid(dn, &sid, "SID");
93 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
94 /* If we fail finding a SID then this is no error since it could
95 * be a non SAM object - e.g. a group with object class
96 * "groupOfNames" */
97 talloc_free(tmp_ctx);
98 return NT_STATUS_OK;
99 } else if (!NT_STATUS_IS_OK(status)) {
100 DEBUG(0, (__location__ ": when parsing DN '%s' we failed to parse it's SID component, so we cannot calculate the group token: %s\n",
101 ldb_dn_get_extended_linearized(tmp_ctx, dn, 1),
102 nt_errstr(status)));
103 talloc_free(tmp_ctx);
104 return status;
107 if (!ldb_dn_minimise(dn)) {
108 talloc_free(tmp_ctx);
109 return NT_STATUS_INTERNAL_DB_CORRUPTION;
112 if (only_childs) {
113 ret = dsdb_search_dn(sam_ctx, tmp_ctx, &res, dn, attrs,
114 DSDB_SEARCH_SHOW_EXTENDED_DN);
115 } else {
116 /* This is an O(n^2) linear search */
117 already_there = sids_contains_sid(*res_sids,
118 *num_res_sids, &sid);
119 if (already_there) {
120 talloc_free(tmp_ctx);
121 return NT_STATUS_OK;
124 ret = dsdb_search(sam_ctx, tmp_ctx, &res, dn, LDB_SCOPE_BASE,
125 attrs, DSDB_SEARCH_SHOW_EXTENDED_DN, "%s",
126 filter);
129 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
130 talloc_free(tmp_ctx);
131 return NT_STATUS_OK;
134 if (ret != LDB_SUCCESS) {
135 DEBUG(1, (__location__ ": dsdb_search for %s failed: %s\n",
136 ldb_dn_get_extended_linearized(tmp_ctx, dn, 1),
137 ldb_errstring(sam_ctx)));
138 talloc_free(tmp_ctx);
139 return NT_STATUS_INTERNAL_DB_CORRUPTION;
142 /* We may get back 0 results, if the SID didn't match the filter - such as it wasn't a domain group, for example */
143 if (res->count != 1) {
144 talloc_free(tmp_ctx);
145 return NT_STATUS_OK;
148 /* We only apply this test once we know the SID matches the filter */
149 if (!only_childs) {
150 *res_sids = talloc_realloc(res_sids_ctx, *res_sids,
151 struct dom_sid, *num_res_sids + 1);
152 NT_STATUS_HAVE_NO_MEMORY_AND_FREE(*res_sids, tmp_ctx);
153 (*res_sids)[*num_res_sids] = sid;
154 ++(*num_res_sids);
157 el = ldb_msg_find_element(res->msgs[0], "memberOf");
159 for (i = 0; el && i < el->num_values; i++) {
160 status = dsdb_expand_nested_groups(sam_ctx, &el->values[i],
161 false, filter, res_sids_ctx, res_sids, num_res_sids);
162 if (!NT_STATUS_IS_OK(status)) {
163 talloc_free(tmp_ctx);
164 return status;
168 talloc_free(tmp_ctx);
170 return NT_STATUS_OK;