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_Principal **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_Principal *groups;
127 struct wbint_Principal *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_Principal *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_Principal *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, g->type);
184 if (subreq == NULL) {
185 return NT_STATUS_NO_MEMORY;
191 static void wb_groups_members_done(struct tevent_req *subreq)
193 struct tevent_req *req = tevent_req_callback_data(
194 subreq, struct tevent_req);
195 struct wb_groups_members_state *state = tevent_req_data(
196 req, struct wb_groups_members_state);
197 int i, num_all_members;
199 struct wbint_Principal *members = NULL;
202 status = wb_lookupgroupmem_recv(subreq, state, &num_members,
207 * In this error handling here we might have to be a bit more generous
208 * and just continue if an error occured.
211 if (!NT_STATUS_IS_OK(status)) {
212 tevent_req_nterror(req, status);
216 num_all_members = talloc_array_length(state->all_members);
218 state->all_members = talloc_realloc(
219 state, state->all_members, struct wbint_Principal,
220 num_all_members + num_members);
221 if ((num_all_members + num_members != 0)
222 && tevent_req_nomem(state->all_members, req)) {
225 for (i=0; i<num_members; i++) {
226 struct wbint_Principal *src, *dst;
228 dst = &state->all_members[num_all_members + i];
229 sid_copy(&dst->sid, &src->sid);
230 dst->name = talloc_move(state->all_members, &src->name);
231 dst->type = src->type;
233 TALLOC_FREE(members);
235 status = wb_groups_members_next_subreq(state, state, &subreq);
236 if (!NT_STATUS_IS_OK(status)) {
237 tevent_req_nterror(req, status);
240 if (subreq == NULL) {
241 tevent_req_done(req);
244 tevent_req_set_callback(subreq, wb_groups_members_done, req);
247 static NTSTATUS wb_groups_members_recv(struct tevent_req *req,
250 struct wbint_Principal **members)
252 struct wb_groups_members_state *state = tevent_req_data(
253 req, struct wb_groups_members_state);
256 if (tevent_req_is_nterror(req, &status)) {
259 *num_members = talloc_array_length(state->all_members);
260 *members = talloc_move(mem_ctx, &state->all_members);
266 * This is the routine expanding a list of groups up to a certain level. We
267 * collect the users in a talloc_dict: We have to add them without duplicates,
268 * and and talloc_dict is an indexed (here indexed by SID) data structure.
271 struct wb_group_members_state {
272 struct tevent_context *ev;
274 struct talloc_dict *users;
275 struct wbint_Principal *groups;
278 static NTSTATUS wb_group_members_next_subreq(
279 struct wb_group_members_state *state,
280 TALLOC_CTX *mem_ctx, struct tevent_req **psubreq);
281 static void wb_group_members_done(struct tevent_req *subreq);
283 struct tevent_req *wb_group_members_send(TALLOC_CTX *mem_ctx,
284 struct tevent_context *ev,
285 const struct dom_sid *sid,
286 enum lsa_SidType type,
289 struct tevent_req *req, *subreq;
290 struct wb_group_members_state *state;
293 req = tevent_req_create(mem_ctx, &state,
294 struct wb_group_members_state);
299 state->depth = max_depth;
300 state->users = talloc_dict_init(state);
301 if (tevent_req_nomem(state->users, req)) {
302 return tevent_req_post(req, ev);
305 state->groups = talloc(state, struct wbint_Principal);
306 if (tevent_req_nomem(state->groups, req)) {
307 return tevent_req_post(req, ev);
309 state->groups->name = NULL;
310 sid_copy(&state->groups->sid, sid);
311 state->groups->type = type;
313 status = wb_group_members_next_subreq(state, state, &subreq);
314 if (!NT_STATUS_IS_OK(status)) {
315 tevent_req_nterror(req, status);
316 return tevent_req_post(req, ev);
318 if (subreq == NULL) {
319 tevent_req_done(req);
320 return tevent_req_post(req, ev);
322 tevent_req_set_callback(subreq, wb_group_members_done, req);
326 static NTSTATUS wb_group_members_next_subreq(
327 struct wb_group_members_state *state,
328 TALLOC_CTX *mem_ctx, struct tevent_req **psubreq)
330 struct tevent_req *subreq;
332 if ((talloc_array_length(state->groups) == 0)
333 || (state->depth <= 0)) {
339 subreq = wb_groups_members_send(
340 mem_ctx, state->ev, talloc_array_length(state->groups),
342 if (subreq == NULL) {
343 return NT_STATUS_NO_MEMORY;
349 static void wb_group_members_done(struct tevent_req *subreq)
351 struct tevent_req *req = tevent_req_callback_data(
352 subreq, struct tevent_req);
353 struct wb_group_members_state *state = tevent_req_data(
354 req, struct wb_group_members_state);
355 int i, num_groups, new_users, new_groups;
357 struct wbint_Principal *members = NULL;
360 status = wb_groups_members_recv(subreq, state, &num_members, &members);
362 if (!NT_STATUS_IS_OK(status)) {
363 tevent_req_nterror(req, status);
367 new_users = new_groups = 0;
368 for (i=0; i<num_members; i++) {
369 switch (members[i].type) {
370 case SID_NAME_DOM_GRP:
372 case SID_NAME_WKN_GRP:
376 /* Ignore everything else */
382 TALLOC_FREE(state->groups);
383 state->groups = talloc_array(state, struct wbint_Principal,
387 * Collect the users into state->users and the groups into
388 * state->groups for the next iteration.
391 for (i=0; i<num_members; i++) {
392 switch (members[i].type) {
394 case SID_NAME_COMPUTER: {
396 * Add a copy of members[i] to state->users
398 struct wbint_Principal *m;
402 m = talloc(talloc_tos(), struct wbint_Principal);
403 if (tevent_req_nomem(m, req)) {
406 sid_copy(&m->sid, &members[i].sid);
407 m->name = talloc_move(m, &members[i].name);
408 m->type = members[i].type;
410 sid = &members[i].sid;
411 key = data_blob_const(
412 sid, ndr_size_dom_sid(sid, NULL, 0));
414 if (!talloc_dict_set(state->users, key, &m)) {
415 tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
420 case SID_NAME_DOM_GRP:
422 case SID_NAME_WKN_GRP: {
423 struct wbint_Principal *g;
425 * Save members[i] for the next round
427 g = &state->groups[num_groups];
428 sid_copy(&g->sid, &members[i].sid);
429 g->name = talloc_move(state->groups, &members[i].name);
430 g->type = members[i].type;
435 /* Ignore everything else */
440 status = wb_group_members_next_subreq(state, state, &subreq);
441 if (!NT_STATUS_IS_OK(status)) {
442 tevent_req_nterror(req, status);
445 if (subreq == NULL) {
446 tevent_req_done(req);
449 tevent_req_set_callback(subreq, wb_group_members_done, req);
452 NTSTATUS wb_group_members_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
453 struct talloc_dict **members)
455 struct wb_group_members_state *state = tevent_req_data(
456 req, struct wb_group_members_state);
459 if (tevent_req_is_nterror(req, &status)) {
462 *members = talloc_move(mem_ctx, &state->users);