2 Unix SMB/CIFS implementation.
3 async lookupgroupmembers
4 Copyright (C) Volker Lendecke 2009
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "librpc/gen_ndr/cli_wbint.h"
25 * We have 3 sets of routines here:
27 * wb_lookupgroupmem is the low-level one-group routine
29 * wb_groups_members walks a list of groups
31 * wb_group_members finally is the high-level routine expanding groups
36 * Look up members of a single group. Essentially a wrapper around the
37 * lookup_groupmem winbindd_methods routine.
40 struct wb_lookupgroupmem_state {
42 struct wbint_GroupMembers members;
45 static void wb_lookupgroupmem_done(struct tevent_req *subreq);
47 static struct tevent_req *wb_lookupgroupmem_send(TALLOC_CTX *mem_ctx,
48 struct tevent_context *ev,
49 const struct dom_sid *group_sid,
50 enum lsa_SidType type)
52 struct tevent_req *req, *subreq;
53 struct wb_lookupgroupmem_state *state;
54 struct winbindd_domain *domain;
56 req = tevent_req_create(mem_ctx, &state,
57 struct wb_lookupgroupmem_state);
61 sid_copy(&state->sid, group_sid);
63 domain = find_domain_from_sid_noinit(group_sid);
65 tevent_req_nterror(req, NT_STATUS_NO_SUCH_GROUP);
66 return tevent_req_post(req, ev);
69 subreq = rpccli_wbint_LookupGroupMembers_send(
70 state, ev, domain->child.rpccli, &state->sid, type,
72 if (tevent_req_nomem(subreq, req)) {
73 return tevent_req_post(req, ev);
75 tevent_req_set_callback(subreq, wb_lookupgroupmem_done, req);
79 static void wb_lookupgroupmem_done(struct tevent_req *subreq)
81 struct tevent_req *req = tevent_req_callback_data(
82 subreq, struct tevent_req);
83 struct wb_lookupgroupmem_state *state = tevent_req_data(
84 req, struct wb_lookupgroupmem_state);
85 NTSTATUS status, result;
87 status = rpccli_wbint_LookupGroupMembers_recv(subreq, state, &result);
89 if (!NT_STATUS_IS_OK(status)) {
90 tevent_req_nterror(req, status);
93 if (!NT_STATUS_IS_OK(result)) {
94 tevent_req_nterror(req, result);
100 static NTSTATUS wb_lookupgroupmem_recv(struct tevent_req *req,
103 struct wbint_GroupMember **members)
105 struct wb_lookupgroupmem_state *state = tevent_req_data(
106 req, struct wb_lookupgroupmem_state);
109 if (tevent_req_is_nterror(req, &status)) {
113 *num_members = state->members.num_members;
114 *members = talloc_move(mem_ctx, &state->members.members);
119 * Same as wb_lookupgroupmem for a list of groups
122 struct wb_groups_members_state {
123 struct tevent_context *ev;
124 struct wbint_GroupMember *groups;
127 struct wbint_GroupMember *all_members;
130 static NTSTATUS wb_groups_members_next_subreq(
131 struct wb_groups_members_state *state,
132 TALLOC_CTX *mem_ctx, struct tevent_req **psubreq);
133 static void wb_groups_members_done(struct tevent_req *subreq);
135 static struct tevent_req *wb_groups_members_send(TALLOC_CTX *mem_ctx,
136 struct tevent_context *ev,
138 struct wbint_GroupMember *groups)
140 struct tevent_req *req, *subreq;
141 struct wb_groups_members_state *state;
144 req = tevent_req_create(mem_ctx, &state,
145 struct wb_groups_members_state);
150 state->groups = groups;
151 state->num_groups = num_groups;
152 state->next_group = 0;
153 state->all_members = NULL;
155 status = wb_groups_members_next_subreq(state, state, &subreq);
156 if (!NT_STATUS_IS_OK(status)) {
157 tevent_req_nterror(req, status);
158 return tevent_req_post(req, ev);
160 if (subreq == NULL) {
161 tevent_req_done(req);
162 return tevent_req_post(req, ev);
164 tevent_req_set_callback(subreq, wb_groups_members_done, req);
168 static NTSTATUS wb_groups_members_next_subreq(
169 struct wb_groups_members_state *state,
170 TALLOC_CTX *mem_ctx, struct tevent_req **psubreq)
172 struct tevent_req *subreq;
173 struct wbint_GroupMember *g;
175 if (state->next_group >= state->num_groups) {
180 g = &state->groups[state->next_group];
181 state->next_group += 1;
183 subreq = wb_lookupgroupmem_send(mem_ctx, state->ev, &g->sid,
185 if (subreq == NULL) {
186 return NT_STATUS_NO_MEMORY;
192 static void wb_groups_members_done(struct tevent_req *subreq)
194 struct tevent_req *req = tevent_req_callback_data(
195 subreq, struct tevent_req);
196 struct wb_groups_members_state *state = tevent_req_data(
197 req, struct wb_groups_members_state);
198 int i, num_all_members;
200 struct wbint_GroupMember *members = NULL;
203 status = wb_lookupgroupmem_recv(subreq, state, &num_members,
208 * In this error handling here we might have to be a bit more generous
209 * and just continue if an error occured.
212 if (!NT_STATUS_IS_OK(status)) {
213 tevent_req_nterror(req, status);
217 num_all_members = talloc_array_length(state->all_members);
219 state->all_members = talloc_realloc(
220 state, state->all_members, struct wbint_GroupMember,
221 num_all_members + num_members);
222 if ((num_all_members + num_members != 0)
223 && tevent_req_nomem(state->all_members, req)) {
226 for (i=0; i<num_members; i++) {
227 struct wbint_GroupMember *src, *dst;
229 dst = &state->all_members[num_all_members + i];
230 sid_copy(&dst->sid, &src->sid);
231 dst->name = talloc_move(state->all_members, &src->name);
232 dst->type = src->type;
234 TALLOC_FREE(members);
236 status = wb_groups_members_next_subreq(state, state, &subreq);
237 if (!NT_STATUS_IS_OK(status)) {
238 tevent_req_nterror(req, status);
241 if (subreq == NULL) {
242 tevent_req_done(req);
245 tevent_req_set_callback(subreq, wb_groups_members_done, req);
248 static NTSTATUS wb_groups_members_recv(struct tevent_req *req,
251 struct wbint_GroupMember **members)
253 struct wb_groups_members_state *state = tevent_req_data(
254 req, struct wb_groups_members_state);
257 if (tevent_req_is_nterror(req, &status)) {
260 *num_members = talloc_array_length(state->all_members);
261 *members = talloc_move(mem_ctx, &state->all_members);
267 * This is the routine expanding a list of groups up to a certain level. We
268 * collect the users in a talloc_dict: We have to add them without duplicates,
269 * and and talloc_dict is an indexed (here indexed by SID) data structure.
272 struct wb_group_members_state {
273 struct tevent_context *ev;
275 struct talloc_dict *users;
276 struct wbint_GroupMember *groups;
279 static NTSTATUS wb_group_members_next_subreq(
280 struct wb_group_members_state *state,
281 TALLOC_CTX *mem_ctx, struct tevent_req **psubreq);
282 static void wb_group_members_done(struct tevent_req *subreq);
284 struct tevent_req *wb_group_members_send(TALLOC_CTX *mem_ctx,
285 struct tevent_context *ev,
286 const struct dom_sid *sid,
287 enum lsa_SidType type,
290 struct tevent_req *req, *subreq;
291 struct wb_group_members_state *state;
294 req = tevent_req_create(mem_ctx, &state,
295 struct wb_group_members_state);
300 state->depth = max_depth;
301 state->users = talloc_dict_init(state);
302 if (tevent_req_nomem(state->users, req)) {
303 return tevent_req_post(req, ev);
306 state->groups = talloc(state, struct wbint_GroupMember);
307 if (tevent_req_nomem(state->groups, req)) {
308 return tevent_req_post(req, ev);
310 state->groups->name = NULL;
311 sid_copy(&state->groups->sid, sid);
312 state->groups->type = type;
314 status = wb_group_members_next_subreq(state, state, &subreq);
315 if (!NT_STATUS_IS_OK(status)) {
316 tevent_req_nterror(req, status);
317 return tevent_req_post(req, ev);
319 if (subreq == NULL) {
320 tevent_req_done(req);
321 return tevent_req_post(req, ev);
323 tevent_req_set_callback(subreq, wb_group_members_done, req);
327 static NTSTATUS wb_group_members_next_subreq(
328 struct wb_group_members_state *state,
329 TALLOC_CTX *mem_ctx, struct tevent_req **psubreq)
331 struct tevent_req *subreq;
333 if ((talloc_array_length(state->groups) == 0)
334 || (state->depth <= 0)) {
340 subreq = wb_groups_members_send(
341 mem_ctx, state->ev, talloc_array_length(state->groups),
343 if (subreq == NULL) {
344 return NT_STATUS_NO_MEMORY;
350 static void wb_group_members_done(struct tevent_req *subreq)
352 struct tevent_req *req = tevent_req_callback_data(
353 subreq, struct tevent_req);
354 struct wb_group_members_state *state = tevent_req_data(
355 req, struct wb_group_members_state);
356 int i, num_groups, new_users, new_groups;
358 struct wbint_GroupMember *members = NULL;
361 status = wb_groups_members_recv(subreq, state, &num_members, &members);
363 if (!NT_STATUS_IS_OK(status)) {
364 tevent_req_nterror(req, status);
368 new_users = new_groups = 0;
369 for (i=0; i<num_members; i++) {
370 switch (members[i].type) {
371 case SID_NAME_DOM_GRP:
373 case SID_NAME_WKN_GRP:
377 /* Ignore everything else */
383 TALLOC_FREE(state->groups);
384 state->groups = talloc_array(state, struct wbint_GroupMember,
388 * Collect the users into state->users and the groups into
389 * state->groups for the next iteration.
392 for (i=0; i<num_members; i++) {
393 switch (members[i].type) {
395 case SID_NAME_COMPUTER: {
397 * Add a copy of members[i] to state->users
399 struct wbint_GroupMember *m;
403 m = talloc(talloc_tos(), struct wbint_GroupMember);
404 if (tevent_req_nomem(m, req)) {
407 sid_copy(&m->sid, &members[i].sid);
408 m->name = talloc_move(m, &members[i].name);
409 m->type = members[i].type;
411 sid = &members[i].sid;
412 key = data_blob_const(
413 sid, ndr_size_dom_sid(sid, NULL, 0));
415 if (!talloc_dict_set(state->users, key, &m)) {
416 tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
421 case SID_NAME_DOM_GRP:
423 case SID_NAME_WKN_GRP: {
424 struct wbint_GroupMember *g;
426 * Save members[i] for the next round
428 g = &state->groups[num_groups];
429 sid_copy(&g->sid, &members[i].sid);
430 g->name = talloc_move(state->groups, &members[i].name);
431 g->type = members[i].type;
436 /* Ignore everything else */
441 status = wb_group_members_next_subreq(state, state, &subreq);
442 if (!NT_STATUS_IS_OK(status)) {
443 tevent_req_nterror(req, status);
446 if (subreq == NULL) {
447 tevent_req_done(req);
450 tevent_req_set_callback(subreq, wb_group_members_done, req);
453 NTSTATUS wb_group_members_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
454 struct talloc_dict **members)
456 struct wb_group_members_state *state = tevent_req_data(
457 req, struct wb_group_members_state);
460 if (tevent_req_is_nterror(req, &status)) {
463 *members = talloc_move(mem_ctx, &state->users);