s3: Remove debug_ctx()
[samba.git] / source3 / winbindd / wb_group_members.c
1 /*
2    Unix SMB/CIFS implementation.
3    async lookupgroupmembers
4    Copyright (C) Volker Lendecke 2009
5
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.
10
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.
15
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/>.
18 */
19
20 #include "includes.h"
21 #include "winbindd.h"
22 #include "librpc/gen_ndr/cli_wbint.h"
23
24 /*
25  * We have 3 sets of routines here:
26  *
27  * wb_lookupgroupmem is the low-level one-group routine
28  *
29  * wb_groups_members walks a list of groups
30  *
31  * wb_group_members finally is the high-level routine expanding groups
32  * recursively
33  */
34
35 /*
36  * TODO: fill_grent_mem_domusers must be re-added
37  */
38
39 /*
40  * Look up members of a single group. Essentially a wrapper around the
41  * lookup_groupmem winbindd_methods routine.
42  */
43
44 struct wb_lookupgroupmem_state {
45         struct dom_sid sid;
46         struct wbint_Principals members;
47 };
48
49 static void wb_lookupgroupmem_done(struct tevent_req *subreq);
50
51 static struct tevent_req *wb_lookupgroupmem_send(TALLOC_CTX *mem_ctx,
52                                                  struct tevent_context *ev,
53                                                  const struct dom_sid *group_sid,
54                                                  enum lsa_SidType type)
55 {
56         struct tevent_req *req, *subreq;
57         struct wb_lookupgroupmem_state *state;
58         struct winbindd_domain *domain;
59
60         req = tevent_req_create(mem_ctx, &state,
61                                 struct wb_lookupgroupmem_state);
62         if (req == NULL) {
63                 return NULL;
64         }
65         sid_copy(&state->sid, group_sid);
66
67         domain = find_domain_from_sid_noinit(group_sid);
68         if (domain == NULL) {
69                 tevent_req_nterror(req, NT_STATUS_NO_SUCH_GROUP);
70                 return tevent_req_post(req, ev);
71         }
72
73         subreq = rpccli_wbint_LookupGroupMembers_send(
74                 state, ev, domain->child.rpccli, &state->sid, type,
75                 &state->members);
76         if (tevent_req_nomem(subreq, req)) {
77                 return tevent_req_post(req, ev);
78         }
79         tevent_req_set_callback(subreq, wb_lookupgroupmem_done, req);
80         return req;
81 }
82
83 static void wb_lookupgroupmem_done(struct tevent_req *subreq)
84 {
85         struct tevent_req *req = tevent_req_callback_data(
86                 subreq, struct tevent_req);
87         struct wb_lookupgroupmem_state *state = tevent_req_data(
88                 req, struct wb_lookupgroupmem_state);
89         NTSTATUS status, result;
90
91         status = rpccli_wbint_LookupGroupMembers_recv(subreq, state, &result);
92         TALLOC_FREE(subreq);
93         if (!NT_STATUS_IS_OK(status)) {
94                 tevent_req_nterror(req, status);
95                 return;
96         }
97         if (!NT_STATUS_IS_OK(result)) {
98                 tevent_req_nterror(req, result);
99                 return;
100         }
101         tevent_req_done(req);
102 }
103
104 static NTSTATUS wb_lookupgroupmem_recv(struct tevent_req *req,
105                                            TALLOC_CTX *mem_ctx,
106                                            int *num_members,
107                                            struct wbint_Principal **members)
108 {
109         struct wb_lookupgroupmem_state *state = tevent_req_data(
110                 req, struct wb_lookupgroupmem_state);
111         NTSTATUS status;
112
113         if (tevent_req_is_nterror(req, &status)) {
114                 return status;
115         }
116
117         *num_members = state->members.num_principals;
118         *members = talloc_move(mem_ctx, &state->members.principals);
119         return NT_STATUS_OK;
120 }
121
122 /*
123  * Same as wb_lookupgroupmem for a list of groups
124  */
125
126 struct wb_groups_members_state {
127         struct tevent_context *ev;
128         struct wbint_Principal *groups;
129         int num_groups;
130         int next_group;
131         struct wbint_Principal *all_members;
132 };
133
134 static NTSTATUS wb_groups_members_next_subreq(
135         struct wb_groups_members_state *state,
136         TALLOC_CTX *mem_ctx, struct tevent_req **psubreq);
137 static void wb_groups_members_done(struct tevent_req *subreq);
138
139 static struct tevent_req *wb_groups_members_send(TALLOC_CTX *mem_ctx,
140                                                  struct tevent_context *ev,
141                                                  int num_groups,
142                                                  struct wbint_Principal *groups)
143 {
144         struct tevent_req *req, *subreq;
145         struct wb_groups_members_state *state;
146         NTSTATUS status;
147
148         req = tevent_req_create(mem_ctx, &state,
149                                 struct wb_groups_members_state);
150         if (req == NULL) {
151                 return NULL;
152         }
153         state->ev = ev;
154         state->groups = groups;
155         state->num_groups = num_groups;
156         state->next_group = 0;
157         state->all_members = NULL;
158
159         status = wb_groups_members_next_subreq(state, state, &subreq);
160         if (!NT_STATUS_IS_OK(status)) {
161                 tevent_req_nterror(req, status);
162                 return tevent_req_post(req, ev);
163         }
164         if (subreq == NULL) {
165                 tevent_req_done(req);
166                 return tevent_req_post(req, ev);
167         }
168         tevent_req_set_callback(subreq, wb_groups_members_done, req);
169         return req;
170 }
171
172 static NTSTATUS wb_groups_members_next_subreq(
173         struct wb_groups_members_state *state,
174         TALLOC_CTX *mem_ctx, struct tevent_req **psubreq)
175 {
176         struct tevent_req *subreq;
177         struct wbint_Principal *g;
178
179         if (state->next_group >= state->num_groups) {
180                 *psubreq = NULL;
181                 return NT_STATUS_OK;
182         }
183
184         g = &state->groups[state->next_group];
185         state->next_group += 1;
186
187         subreq = wb_lookupgroupmem_send(mem_ctx, state->ev, &g->sid, g->type);
188         if (subreq == NULL) {
189                 return NT_STATUS_NO_MEMORY;
190         }
191         *psubreq = subreq;
192         return NT_STATUS_OK;
193 }
194
195 static void wb_groups_members_done(struct tevent_req *subreq)
196 {
197         struct tevent_req *req = tevent_req_callback_data(
198                 subreq, struct tevent_req);
199         struct wb_groups_members_state *state = tevent_req_data(
200                 req, struct wb_groups_members_state);
201         int i, num_all_members;
202         int num_members = 0;
203         struct wbint_Principal *members = NULL;
204         NTSTATUS status;
205
206         status = wb_lookupgroupmem_recv(subreq, state, &num_members,
207                                             &members);
208         TALLOC_FREE(subreq);
209
210         /*
211          * In this error handling here we might have to be a bit more generous
212          * and just continue if an error occured.
213          */
214
215         if (!NT_STATUS_IS_OK(status)) {
216                 tevent_req_nterror(req, status);
217                 return;
218         }
219
220         num_all_members = talloc_array_length(state->all_members);
221
222         state->all_members = talloc_realloc(
223                 state, state->all_members, struct wbint_Principal,
224                 num_all_members + num_members);
225         if ((num_all_members + num_members != 0)
226             && tevent_req_nomem(state->all_members, req)) {
227                 return;
228         }
229         for (i=0; i<num_members; i++) {
230                 struct wbint_Principal *src, *dst;
231                 src = &members[i];
232                 dst = &state->all_members[num_all_members + i];
233                 sid_copy(&dst->sid, &src->sid);
234                 dst->name = talloc_move(state->all_members, &src->name);
235                 dst->type = src->type;
236         }
237         TALLOC_FREE(members);
238
239         status = wb_groups_members_next_subreq(state, state, &subreq);
240         if (!NT_STATUS_IS_OK(status)) {
241                 tevent_req_nterror(req, status);
242                 return;
243         }
244         if (subreq == NULL) {
245                 tevent_req_done(req);
246                 return;
247         }
248         tevent_req_set_callback(subreq, wb_groups_members_done, req);
249 }
250
251 static NTSTATUS wb_groups_members_recv(struct tevent_req *req,
252                                        TALLOC_CTX *mem_ctx,
253                                        int *num_members,
254                                        struct wbint_Principal **members)
255 {
256         struct wb_groups_members_state *state = tevent_req_data(
257                 req, struct wb_groups_members_state);
258         NTSTATUS status;
259
260         if (tevent_req_is_nterror(req, &status)) {
261                 return status;
262         }
263         *num_members = talloc_array_length(state->all_members);
264         *members = talloc_move(mem_ctx, &state->all_members);
265         return NT_STATUS_OK;
266 }
267
268
269 /*
270  * This is the routine expanding a list of groups up to a certain level. We
271  * collect the users in a talloc_dict: We have to add them without duplicates,
272  * and and talloc_dict is an indexed (here indexed by SID) data structure.
273  */
274
275 struct wb_group_members_state {
276         struct tevent_context *ev;
277         int depth;
278         struct talloc_dict *users;
279         struct wbint_Principal *groups;
280 };
281
282 static NTSTATUS wb_group_members_next_subreq(
283         struct wb_group_members_state *state,
284         TALLOC_CTX *mem_ctx, struct tevent_req **psubreq);
285 static void wb_group_members_done(struct tevent_req *subreq);
286
287 struct tevent_req *wb_group_members_send(TALLOC_CTX *mem_ctx,
288                                          struct tevent_context *ev,
289                                          const struct dom_sid *sid,
290                                          enum lsa_SidType type,
291                                          int max_depth)
292 {
293         struct tevent_req *req, *subreq;
294         struct wb_group_members_state *state;
295         NTSTATUS status;
296
297         req = tevent_req_create(mem_ctx, &state,
298                                 struct wb_group_members_state);
299         if (req == NULL) {
300                 return NULL;
301         }
302         state->ev = ev;
303         state->depth = max_depth;
304         state->users = talloc_dict_init(state);
305         if (tevent_req_nomem(state->users, req)) {
306                 return tevent_req_post(req, ev);
307         }
308
309         state->groups = talloc(state, struct wbint_Principal);
310         if (tevent_req_nomem(state->groups, req)) {
311                 return tevent_req_post(req, ev);
312         }
313         state->groups->name = NULL;
314         sid_copy(&state->groups->sid, sid);
315         state->groups->type = type;
316
317         status = wb_group_members_next_subreq(state, state, &subreq);
318         if (!NT_STATUS_IS_OK(status)) {
319                 tevent_req_nterror(req, status);
320                 return tevent_req_post(req, ev);
321         }
322         if (subreq == NULL) {
323                 tevent_req_done(req);
324                 return tevent_req_post(req, ev);
325         }
326         tevent_req_set_callback(subreq, wb_group_members_done, req);
327         return req;
328 }
329
330 static NTSTATUS wb_group_members_next_subreq(
331         struct wb_group_members_state *state,
332         TALLOC_CTX *mem_ctx, struct tevent_req **psubreq)
333 {
334         struct tevent_req *subreq;
335
336         if ((talloc_array_length(state->groups) == 0)
337             || (state->depth <= 0)) {
338                 *psubreq = NULL;
339                 return NT_STATUS_OK;
340         }
341         state->depth -= 1;
342
343         subreq = wb_groups_members_send(
344                 mem_ctx, state->ev, talloc_array_length(state->groups),
345                 state->groups);
346         if (subreq == NULL) {
347                 return NT_STATUS_NO_MEMORY;
348         }
349         *psubreq = subreq;
350         return NT_STATUS_OK;
351 }
352
353 static void wb_group_members_done(struct tevent_req *subreq)
354 {
355         struct tevent_req *req = tevent_req_callback_data(
356                 subreq, struct tevent_req);
357         struct wb_group_members_state *state = tevent_req_data(
358                 req, struct wb_group_members_state);
359         int i, num_groups, new_users, new_groups;
360         int num_members = 0;
361         struct wbint_Principal *members = NULL;
362         NTSTATUS status;
363
364         status = wb_groups_members_recv(subreq, state, &num_members, &members);
365         TALLOC_FREE(subreq);
366         if (!NT_STATUS_IS_OK(status)) {
367                 tevent_req_nterror(req, status);
368                 return;
369         }
370
371         new_users = new_groups = 0;
372         for (i=0; i<num_members; i++) {
373                 switch (members[i].type) {
374                 case SID_NAME_DOM_GRP:
375                 case SID_NAME_ALIAS:
376                 case SID_NAME_WKN_GRP:
377                         new_groups += 1;
378                         break;
379                 default:
380                         /* Ignore everything else */
381                         break;
382                 }
383         }
384
385         num_groups = 0;
386         TALLOC_FREE(state->groups);
387         state->groups = talloc_array(state, struct wbint_Principal,
388                                      new_groups);
389
390         /*
391          * Collect the users into state->users and the groups into
392          * state->groups for the next iteration.
393          */
394
395         for (i=0; i<num_members; i++) {
396                 switch (members[i].type) {
397                 case SID_NAME_USER:
398                 case SID_NAME_COMPUTER: {
399                         /*
400                          * Add a copy of members[i] to state->users
401                          */
402                         struct wbint_Principal *m;
403                         struct dom_sid *sid;
404                         DATA_BLOB key;
405
406                         m = talloc(talloc_tos(), struct wbint_Principal);
407                         if (tevent_req_nomem(m, req)) {
408                                 return;
409                         }
410                         sid_copy(&m->sid, &members[i].sid);
411                         m->name = talloc_move(m, &members[i].name);
412                         m->type = members[i].type;
413
414                         sid = &members[i].sid;
415                         key = data_blob_const(
416                                 sid, ndr_size_dom_sid(sid, NULL, 0));
417
418                         if (!talloc_dict_set(state->users, key, &m)) {
419                                 tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
420                                 return;
421                         }
422                         break;
423                 }
424                 case SID_NAME_DOM_GRP:
425                 case SID_NAME_ALIAS:
426                 case SID_NAME_WKN_GRP: {
427                         struct wbint_Principal *g;
428                         /*
429                          * Save members[i] for the next round
430                          */
431                         g = &state->groups[num_groups];
432                         sid_copy(&g->sid, &members[i].sid);
433                         g->name = talloc_move(state->groups, &members[i].name);
434                         g->type = members[i].type;
435                         num_groups += 1;
436                         break;
437                 }
438                 default:
439                         /* Ignore everything else */
440                         break;
441                 }
442         }
443
444         status = wb_group_members_next_subreq(state, state, &subreq);
445         if (!NT_STATUS_IS_OK(status)) {
446                 tevent_req_nterror(req, status);
447                 return;
448         }
449         if (subreq == NULL) {
450                 tevent_req_done(req);
451                 return;
452         }
453         tevent_req_set_callback(subreq, wb_group_members_done, req);
454 }
455
456 NTSTATUS wb_group_members_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
457                                struct talloc_dict **members)
458 {
459         struct wb_group_members_state *state = tevent_req_data(
460                 req, struct wb_group_members_state);
461         NTSTATUS status;
462
463         if (tevent_req_is_nterror(req, &status)) {
464                 return status;
465         }
466         *members = talloc_move(mem_ctx, &state->users);
467         return NT_STATUS_OK;
468 }