Revert "s3/service: convert lp_force_group() to const"
[Samba.git] / source3 / winbindd / wb_group_members.c
blob7978b188143e6fb0dac05f5c553e67219d0cd7c4
1 /*
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/>.
20 #include "includes.h"
21 #include "winbindd.h"
22 #include "librpc/gen_ndr/ndr_winbind_c.h"
23 #include "../librpc/gen_ndr/ndr_security.h"
24 #include "../libcli/security/security.h"
25 #include "lib/util/util_tdb.h"
26 #include "lib/dbwrap/dbwrap.h"
27 #include "lib/dbwrap/dbwrap_rbt.h"
30 * We have 3 sets of routines here:
32 * wb_lookupgroupmem is the low-level one-group routine
34 * wb_groups_members walks a list of groups
36 * wb_group_members finally is the high-level routine expanding groups
37 * recursively
41 * TODO: fill_grent_mem_domusers must be re-added
45 * Look up members of a single group. Essentially a wrapper around the
46 * lookup_groupmem winbindd_methods routine.
49 struct wb_lookupgroupmem_state {
50 struct dom_sid sid;
51 struct wbint_Principals members;
54 static void wb_lookupgroupmem_done(struct tevent_req *subreq);
56 static struct tevent_req *wb_lookupgroupmem_send(TALLOC_CTX *mem_ctx,
57 struct tevent_context *ev,
58 const struct dom_sid *group_sid,
59 enum lsa_SidType type)
61 struct tevent_req *req, *subreq;
62 struct wb_lookupgroupmem_state *state;
63 struct winbindd_domain *domain;
65 req = tevent_req_create(mem_ctx, &state,
66 struct wb_lookupgroupmem_state);
67 if (req == NULL) {
68 return NULL;
70 sid_copy(&state->sid, group_sid);
72 domain = find_domain_from_sid_noinit(group_sid);
73 if (domain == NULL) {
74 tevent_req_nterror(req, NT_STATUS_NO_SUCH_GROUP);
75 return tevent_req_post(req, ev);
78 subreq = dcerpc_wbint_LookupGroupMembers_send(
79 state, ev, dom_child_handle(domain), &state->sid, type,
80 &state->members);
81 if (tevent_req_nomem(subreq, req)) {
82 return tevent_req_post(req, ev);
84 tevent_req_set_callback(subreq, wb_lookupgroupmem_done, req);
85 return req;
88 static void wb_lookupgroupmem_done(struct tevent_req *subreq)
90 struct tevent_req *req = tevent_req_callback_data(
91 subreq, struct tevent_req);
92 struct wb_lookupgroupmem_state *state = tevent_req_data(
93 req, struct wb_lookupgroupmem_state);
94 NTSTATUS status, result;
96 status = dcerpc_wbint_LookupGroupMembers_recv(subreq, state, &result);
97 TALLOC_FREE(subreq);
98 if (any_nt_status_not_ok(status, result, &status)) {
99 tevent_req_nterror(req, status);
100 return;
102 tevent_req_done(req);
105 static NTSTATUS wb_lookupgroupmem_recv(struct tevent_req *req,
106 TALLOC_CTX *mem_ctx,
107 int *num_members,
108 struct wbint_Principal **members)
110 struct wb_lookupgroupmem_state *state = tevent_req_data(
111 req, struct wb_lookupgroupmem_state);
112 NTSTATUS status;
114 if (tevent_req_is_nterror(req, &status)) {
115 return status;
118 *num_members = state->members.num_principals;
119 *members = talloc_move(mem_ctx, &state->members.principals);
120 return NT_STATUS_OK;
124 * Same as wb_lookupgroupmem for a list of groups
127 struct wb_groups_members_state {
128 struct tevent_context *ev;
129 struct wbint_Principal *groups;
130 int num_groups;
131 int next_group;
132 struct wbint_Principal *all_members;
135 static NTSTATUS wb_groups_members_next_subreq(
136 struct wb_groups_members_state *state,
137 TALLOC_CTX *mem_ctx, struct tevent_req **psubreq);
138 static void wb_groups_members_done(struct tevent_req *subreq);
140 static struct tevent_req *wb_groups_members_send(TALLOC_CTX *mem_ctx,
141 struct tevent_context *ev,
142 int num_groups,
143 struct wbint_Principal *groups)
145 struct tevent_req *req, *subreq = NULL;
146 struct wb_groups_members_state *state;
147 NTSTATUS status;
149 req = tevent_req_create(mem_ctx, &state,
150 struct wb_groups_members_state);
151 if (req == NULL) {
152 return NULL;
154 state->ev = ev;
155 state->groups = groups;
156 state->num_groups = num_groups;
157 state->next_group = 0;
158 state->all_members = NULL;
160 status = wb_groups_members_next_subreq(state, state, &subreq);
161 if (tevent_req_nterror(req, status)) {
162 return tevent_req_post(req, ev);
164 if (subreq == NULL) {
165 tevent_req_done(req);
166 return tevent_req_post(req, ev);
168 tevent_req_set_callback(subreq, wb_groups_members_done, req);
169 return req;
172 static NTSTATUS wb_groups_members_next_subreq(
173 struct wb_groups_members_state *state,
174 TALLOC_CTX *mem_ctx, struct tevent_req **psubreq)
176 struct tevent_req *subreq;
177 struct wbint_Principal *g;
179 if (state->next_group >= state->num_groups) {
180 *psubreq = NULL;
181 return NT_STATUS_OK;
184 g = &state->groups[state->next_group];
185 state->next_group += 1;
187 subreq = wb_lookupgroupmem_send(mem_ctx, state->ev, &g->sid, g->type);
188 if (subreq == NULL) {
189 return NT_STATUS_NO_MEMORY;
191 *psubreq = subreq;
192 return NT_STATUS_OK;
195 static void wb_groups_members_done(struct tevent_req *subreq)
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;
206 status = wb_lookupgroupmem_recv(subreq, state, &num_members,
207 &members);
208 TALLOC_FREE(subreq);
211 * In this error handling here we might have to be a bit more generous
212 * and just continue if an error occurred.
215 if (!NT_STATUS_IS_OK(status)) {
216 if (!NT_STATUS_EQUAL(
217 status, NT_STATUS_TRUSTED_DOMAIN_FAILURE)) {
218 tevent_req_nterror(req, status);
219 return;
221 num_members = 0;
224 num_all_members = talloc_array_length(state->all_members);
226 state->all_members = talloc_realloc(
227 state, state->all_members, struct wbint_Principal,
228 num_all_members + num_members);
229 if ((num_all_members + num_members != 0)
230 && tevent_req_nomem(state->all_members, req)) {
231 return;
233 for (i=0; i<num_members; i++) {
234 struct wbint_Principal *src, *dst;
235 src = &members[i];
236 dst = &state->all_members[num_all_members + i];
237 sid_copy(&dst->sid, &src->sid);
238 dst->name = talloc_move(state->all_members, &src->name);
239 dst->type = src->type;
241 TALLOC_FREE(members);
243 status = wb_groups_members_next_subreq(state, state, &subreq);
244 if (tevent_req_nterror(req, status)) {
245 return;
247 if (subreq == NULL) {
248 tevent_req_done(req);
249 return;
251 tevent_req_set_callback(subreq, wb_groups_members_done, req);
254 static NTSTATUS wb_groups_members_recv(struct tevent_req *req,
255 TALLOC_CTX *mem_ctx,
256 int *num_members,
257 struct wbint_Principal **members)
259 struct wb_groups_members_state *state = tevent_req_data(
260 req, struct wb_groups_members_state);
261 NTSTATUS status;
263 if (tevent_req_is_nterror(req, &status)) {
264 return status;
266 *num_members = talloc_array_length(state->all_members);
267 *members = talloc_move(mem_ctx, &state->all_members);
268 return NT_STATUS_OK;
273 * This is the routine expanding a list of groups up to a certain level. We
274 * collect the users in a rbt database: We have to add them without duplicates,
275 * and the db is indexed by SID.
278 struct wb_group_members_state {
279 struct tevent_context *ev;
280 int depth;
281 struct db_context *users;
282 struct wbint_Principal *groups;
285 static NTSTATUS wb_group_members_next_subreq(
286 struct wb_group_members_state *state,
287 TALLOC_CTX *mem_ctx, struct tevent_req **psubreq);
288 static void wb_group_members_done(struct tevent_req *subreq);
290 struct tevent_req *wb_group_members_send(TALLOC_CTX *mem_ctx,
291 struct tevent_context *ev,
292 const struct dom_sid *sid,
293 enum lsa_SidType type,
294 int max_depth)
296 struct tevent_req *req, *subreq = NULL;
297 struct wb_group_members_state *state;
298 NTSTATUS status;
300 req = tevent_req_create(mem_ctx, &state,
301 struct wb_group_members_state);
302 if (req == NULL) {
303 return NULL;
305 state->ev = ev;
306 state->depth = max_depth;
307 state->users = db_open_rbt(state);
308 if (tevent_req_nomem(state->users, req)) {
309 return tevent_req_post(req, ev);
312 state->groups = talloc(state, struct wbint_Principal);
313 if (tevent_req_nomem(state->groups, req)) {
314 return tevent_req_post(req, ev);
316 state->groups->name = NULL;
317 sid_copy(&state->groups->sid, sid);
318 state->groups->type = type;
320 status = wb_group_members_next_subreq(state, state, &subreq);
321 if (tevent_req_nterror(req, status)) {
322 return tevent_req_post(req, ev);
324 if (subreq == NULL) {
325 tevent_req_done(req);
326 return tevent_req_post(req, ev);
328 tevent_req_set_callback(subreq, wb_group_members_done, req);
329 return req;
332 static NTSTATUS wb_group_members_next_subreq(
333 struct wb_group_members_state *state,
334 TALLOC_CTX *mem_ctx, struct tevent_req **psubreq)
336 struct tevent_req *subreq;
338 if ((talloc_array_length(state->groups) == 0)
339 || (state->depth <= 0)) {
340 *psubreq = NULL;
341 return NT_STATUS_OK;
343 state->depth -= 1;
345 subreq = wb_groups_members_send(
346 mem_ctx, state->ev, talloc_array_length(state->groups),
347 state->groups);
348 if (subreq == NULL) {
349 return NT_STATUS_NO_MEMORY;
351 *psubreq = subreq;
352 return NT_STATUS_OK;
355 NTSTATUS add_member_to_db(struct db_context *db, struct dom_sid *sid,
356 const char *name)
358 size_t len = ndr_size_dom_sid(sid, 0);
359 uint8_t sidbuf[len];
360 TDB_DATA key = { .dptr = sidbuf, .dsize = sizeof(sidbuf) };
361 NTSTATUS status;
363 sid_linearize(sidbuf, sizeof(sidbuf), sid);
365 status = dbwrap_store(db, key, string_term_tdb_data(name), 0);
366 return status;
369 static void wb_group_members_done(struct tevent_req *subreq)
371 struct tevent_req *req = tevent_req_callback_data(
372 subreq, struct tevent_req);
373 struct wb_group_members_state *state = tevent_req_data(
374 req, struct wb_group_members_state);
375 int i, num_groups, new_groups;
376 int num_members = 0;
377 struct wbint_Principal *members = NULL;
378 NTSTATUS status;
380 status = wb_groups_members_recv(subreq, state, &num_members, &members);
381 TALLOC_FREE(subreq);
382 if (tevent_req_nterror(req, status)) {
383 return;
386 new_groups = 0;
387 for (i=0; i<num_members; i++) {
388 switch (members[i].type) {
389 case SID_NAME_DOM_GRP:
390 case SID_NAME_ALIAS:
391 case SID_NAME_WKN_GRP:
392 new_groups += 1;
393 break;
394 default:
395 /* Ignore everything else */
396 break;
400 num_groups = 0;
401 TALLOC_FREE(state->groups);
402 state->groups = talloc_array(state, struct wbint_Principal,
403 new_groups);
406 * Collect the users into state->users and the groups into
407 * state->groups for the next iteration.
410 for (i=0; i<num_members; i++) {
411 switch (members[i].type) {
412 case SID_NAME_USER:
413 case SID_NAME_COMPUTER: {
415 * Add a copy of members[i] to state->users
417 status = add_member_to_db(state->users, &members[i].sid,
418 members[i].name);
419 if (tevent_req_nterror(req, status)) {
420 return;
423 break;
425 case SID_NAME_DOM_GRP:
426 case SID_NAME_ALIAS:
427 case SID_NAME_WKN_GRP: {
428 struct wbint_Principal *g;
430 * Save members[i] for the next round
432 g = &state->groups[num_groups];
433 sid_copy(&g->sid, &members[i].sid);
434 g->name = talloc_move(state->groups, &members[i].name);
435 g->type = members[i].type;
436 num_groups += 1;
437 break;
439 default:
440 /* Ignore everything else */
441 break;
445 status = wb_group_members_next_subreq(state, state, &subreq);
446 if (tevent_req_nterror(req, status)) {
447 return;
449 if (subreq == NULL) {
450 tevent_req_done(req);
451 return;
453 tevent_req_set_callback(subreq, wb_group_members_done, req);
456 NTSTATUS wb_group_members_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
457 struct db_context **members)
459 struct wb_group_members_state *state = tevent_req_data(
460 req, struct wb_group_members_state);
461 NTSTATUS status;
463 if (tevent_req_is_nterror(req, &status)) {
464 return status;
466 *members = talloc_move(mem_ctx, &state->users);
467 return NT_STATUS_OK;