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/>.
23 #include "auth/auth.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
)
36 for (i
= 0; i
< num_sids
; i
++) {
37 if (dom_sid_equal(&sids
[i
], sid
))
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
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
};
69 struct ldb_result
*res
;
71 const struct ldb_message_element
*el
;
73 if (*res_sids
== NULL
) {
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
);
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
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),
103 talloc_free(tmp_ctx
);
107 if (!ldb_dn_minimise(dn
)) {
108 talloc_free(tmp_ctx
);
109 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
113 ret
= dsdb_search_dn(sam_ctx
, tmp_ctx
, &res
, dn
, attrs
,
114 DSDB_SEARCH_SHOW_EXTENDED_DN
);
116 /* This is an O(n^2) linear search */
117 already_there
= sids_contains_sid(*res_sids
,
118 *num_res_sids
, &sid
);
120 talloc_free(tmp_ctx
);
124 ret
= dsdb_search(sam_ctx
, tmp_ctx
, &res
, dn
, LDB_SCOPE_BASE
,
125 attrs
, DSDB_SEARCH_SHOW_EXTENDED_DN
, "%s",
129 if (ret
== LDB_ERR_NO_SUCH_OBJECT
) {
130 talloc_free(tmp_ctx
);
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
);
148 /* We only apply this test once we know the SID matches the filter */
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
;
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
);
168 talloc_free(tmp_ctx
);