s4:rpc_srv:getncchanges: 4.5 anc emulation uses qsort(), not ldb_qsort()
[samba.git] / source3 / winbindd / wb_getgrsid.c
blob4fd696dfa103bd7279d8ebf88d0c448a7f81a94e
1 /*
2 Unix SMB/CIFS implementation.
3 async getgrsid
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 "../libcli/security/security.h"
24 #include "lib/dbwrap/dbwrap_rbt.h"
25 #include "lib/dbwrap/dbwrap.h"
27 struct wb_getgrsid_state {
28 struct tevent_context *ev;
29 struct dom_sid sid;
30 int max_nesting;
31 const char *domname;
32 const char *name;
33 enum lsa_SidType type;
34 gid_t gid;
35 struct db_context *members;
36 uint32_t num_sids;
37 struct dom_sid *sids;
40 static void wb_getgrsid_lookupsid_done(struct tevent_req *subreq);
41 static void wb_getgrsid_sid2gid_done(struct tevent_req *subreq);
42 static void wb_getgrsid_got_members(struct tevent_req *subreq);
43 static void wb_getgrsid_got_alias_members(struct tevent_req *subreq);
45 struct tevent_req *wb_getgrsid_send(TALLOC_CTX *mem_ctx,
46 struct tevent_context *ev,
47 const struct dom_sid *group_sid,
48 int max_nesting)
50 struct tevent_req *req, *subreq;
51 struct wb_getgrsid_state *state;
52 struct dom_sid_buf buf;
54 req = tevent_req_create(mem_ctx, &state, struct wb_getgrsid_state);
55 if (req == NULL) {
56 return NULL;
59 D_INFO("WB command getgrsid start.\nLooking up group SID %s.\n", dom_sid_str_buf(group_sid, &buf));
61 sid_copy(&state->sid, group_sid);
62 state->ev = ev;
63 state->max_nesting = max_nesting;
65 if (dom_sid_in_domain(&global_sid_Unix_Groups, group_sid)) {
66 /* unmapped Unix groups must be resolved locally */
67 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
68 return tevent_req_post(req, ev);
71 subreq = wb_lookupsid_send(state, ev, &state->sid);
72 if (tevent_req_nomem(subreq, req)) {
73 return tevent_req_post(req, ev);
75 tevent_req_set_callback(subreq, wb_getgrsid_lookupsid_done, req);
76 return req;
79 static void wb_getgrsid_lookupsid_done(struct tevent_req *subreq)
81 struct tevent_req *req = tevent_req_callback_data(
82 subreq, struct tevent_req);
83 struct wb_getgrsid_state *state = tevent_req_data(
84 req, struct wb_getgrsid_state);
85 NTSTATUS status;
87 status = wb_lookupsid_recv(subreq, state, &state->type,
88 &state->domname, &state->name);
89 TALLOC_FREE(subreq);
90 if (tevent_req_nterror(req, status)) {
91 return;
94 switch (state->type) {
95 case SID_NAME_DOM_GRP:
96 case SID_NAME_ALIAS:
97 case SID_NAME_WKN_GRP:
99 * also treat user-type SIDS (they might map to ID_TYPE_BOTH)
101 case SID_NAME_USER:
102 case SID_NAME_COMPUTER:
103 break;
104 default:
105 tevent_req_nterror(req, NT_STATUS_NO_SUCH_GROUP);
106 return;
109 subreq = wb_sids2xids_send(state, state->ev, &state->sid, 1);
110 if (tevent_req_nomem(subreq, req)) {
111 return;
113 tevent_req_set_callback(subreq, wb_getgrsid_sid2gid_done, req);
116 static void wb_getgrsid_sid2gid_done(struct tevent_req *subreq)
118 struct tevent_req *req = tevent_req_callback_data(
119 subreq, struct tevent_req);
120 struct wb_getgrsid_state *state = tevent_req_data(
121 req, struct wb_getgrsid_state);
122 NTSTATUS status;
123 struct unixid xids[1];
125 status = wb_sids2xids_recv(subreq, xids, ARRAY_SIZE(xids));
126 TALLOC_FREE(subreq);
127 if (tevent_req_nterror(req, status)) {
128 return;
132 * We are filtering further down in sids2xids, but that filtering
133 * depends on the actual type of the sid handed in (as determined
134 * by lookupsids). Here we need to filter for the type of object
135 * actually requested, in this case uid.
137 if (!(xids[0].type == ID_TYPE_GID || xids[0].type == ID_TYPE_BOTH)) {
138 tevent_req_nterror(req, NT_STATUS_NONE_MAPPED);
139 return;
142 state->gid = (gid_t)xids[0].id;
144 switch (state->type) {
145 case SID_NAME_USER:
146 case SID_NAME_COMPUTER: {
148 * special treatment for a user sid that is
149 * mapped to ID_TYPE_BOTH:
150 * create a group with the sid/xid as only member
152 const char *name;
154 if (xids[0].type != ID_TYPE_BOTH) {
155 tevent_req_nterror(req, NT_STATUS_NO_SUCH_GROUP);
156 return;
159 state->members = db_open_rbt(state);
160 if (tevent_req_nomem(state->members, req)) {
161 return;
164 name = fill_domain_username_talloc(talloc_tos(),
165 state->domname,
166 state->name,
167 true /* can_assume */);
168 if (tevent_req_nomem(name, req)) {
169 return;
172 status = add_member_to_db(state->members, &state->sid, name);
173 if (!NT_STATUS_IS_OK(status)) {
174 tevent_req_nterror(req, status);
175 return;
178 tevent_req_done(req);
179 return;
181 case SID_NAME_ALIAS:
182 subreq = wb_alias_members_send(state,
183 state->ev,
184 &state->sid,
185 state->type,
186 state->max_nesting);
187 if (tevent_req_nomem(subreq, req)) {
188 return;
190 /* Decrement the depth based on 'winbind expand groups' */
191 state->max_nesting--;
192 tevent_req_set_callback(subreq,
193 wb_getgrsid_got_alias_members,
194 req);
195 break;
196 case SID_NAME_DOM_GRP:
197 subreq = wb_group_members_send(state,
198 state->ev,
199 &state->sid,
201 &state->type,
202 state->max_nesting);
203 if (tevent_req_nomem(subreq, req)) {
204 return;
206 tevent_req_set_callback(subreq, wb_getgrsid_got_members, req);
207 break;
208 case SID_NAME_WKN_GRP:
209 state->members = db_open_rbt(state);
210 if (tevent_req_nomem(state->members, req)) {
211 return;
213 tevent_req_done(req);
214 return;
215 default:
216 tevent_req_nterror(req, NT_STATUS_NO_SUCH_GROUP);
217 break;
221 static void wb_getgrsid_got_alias_members_names(struct tevent_req *subreq)
223 struct tevent_req *req =
224 tevent_req_callback_data(subreq, struct tevent_req);
225 struct wb_getgrsid_state *state =
226 tevent_req_data(req, struct wb_getgrsid_state);
227 struct lsa_RefDomainList *domains = NULL;
228 struct lsa_TransNameArray *names = NULL;
229 NTSTATUS status;
230 uint32_t li;
231 uint32_t num_sids = 0;
232 struct dom_sid *sids = NULL;
233 enum lsa_SidType *types = NULL;
235 status = wb_lookupsids_recv(subreq, state, &domains, &names);
237 TALLOC_FREE(subreq);
238 if (tevent_req_nterror(req, status)) {
239 D_WARNING("Failed with %s.\n", nt_errstr(status));
240 return;
243 if (domains == NULL) {
244 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
245 D_WARNING("Failed with NT_STATUS_INTERNAL_ERROR.\n");
246 return;
249 if (names == NULL) {
250 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
251 D_WARNING("Failed with NT_STATUS_INTERNAL_ERROR.\n");
252 return;
255 state->members = db_open_rbt(state);
256 if (tevent_req_nomem(state->members, req)) {
257 return;
260 for (li = 0; li < state->num_sids; li++) {
261 struct lsa_TranslatedName *n = &names->names[li];
263 if (n->sid_type == SID_NAME_USER ||
264 n->sid_type == SID_NAME_COMPUTER) {
265 const char *name = fill_domain_username_talloc(
266 talloc_tos(),
267 domains->domains[n->sid_index].name.string,
268 n->name.string,
269 false /* can_assume */);
270 if (tevent_req_nomem(name, req)) {
271 return;
274 status = add_member_to_db(state->members,
275 &state->sids[li],
276 name);
277 if (!NT_STATUS_IS_OK(status)) {
278 tevent_req_nterror(req, status);
279 return;
281 } else if (n->sid_type == SID_NAME_DOM_GRP) {
282 sids = talloc_realloc(talloc_tos(),
283 sids,
284 struct dom_sid,
285 num_sids + 1);
286 if (tevent_req_nomem(sids, req)) {
287 return;
289 sids[num_sids] = state->sids[li];
290 types = talloc_realloc(talloc_tos(),
291 types,
292 enum lsa_SidType,
293 num_sids + 1);
294 if (tevent_req_nomem(types, req)) {
295 return;
297 types[num_sids] = n->sid_type;
298 num_sids++;
299 } else {
300 struct dom_sid_buf buf;
301 D_DEBUG("SID %s with sid_type=%d is ignored!\n",
302 dom_sid_str_buf(&state->sids[li], &buf),
303 n->sid_type);
307 TALLOC_FREE(names);
308 TALLOC_FREE(domains);
310 if (num_sids == 0) {
311 tevent_req_done(req);
312 return;
314 subreq = wb_group_members_send(state,
315 state->ev,
316 sids,
317 num_sids,
318 types,
319 state->max_nesting);
320 if (tevent_req_nomem(subreq, req)) {
321 return;
323 tevent_req_set_callback(subreq, wb_getgrsid_got_members, req);
326 static void wb_getgrsid_got_alias_members(struct tevent_req *subreq)
328 struct tevent_req *req =
329 tevent_req_callback_data(subreq, struct tevent_req);
330 struct wb_getgrsid_state *state =
331 tevent_req_data(req, struct wb_getgrsid_state);
332 NTSTATUS status;
334 status = wb_alias_members_recv(subreq,
335 state,
336 &state->num_sids,
337 &state->sids);
338 TALLOC_FREE(subreq);
339 if (tevent_req_nterror(req, status)) {
340 return;
343 subreq = wb_lookupsids_send(state,
344 state->ev,
345 state->sids,
346 state->num_sids);
347 if (tevent_req_nomem(subreq, req)) {
348 return;
350 tevent_req_set_callback(subreq,
351 wb_getgrsid_got_alias_members_names,
352 req);
355 static void wb_getgrsid_got_members(struct tevent_req *subreq)
357 struct tevent_req *req = tevent_req_callback_data(
358 subreq, struct tevent_req);
359 struct wb_getgrsid_state *state = tevent_req_data(
360 req, struct wb_getgrsid_state);
361 NTSTATUS status;
362 struct db_context *members_prev = state->members;
364 status = wb_group_members_recv(subreq, state, &state->members);
365 TALLOC_FREE(subreq);
366 if (tevent_req_nterror(req, status)) {
367 return;
370 * If we have called wb_alias_members_send(), members_prev
371 * might already contain users that are direct members of alias,
372 * add to them the users from nested groups.
374 if (members_prev != NULL) {
375 status = dbwrap_merge_dbs(state->members,
376 members_prev,
377 TDB_REPLACE);
378 if (tevent_req_nterror(req, status)) {
379 return;
382 tevent_req_done(req);
385 NTSTATUS wb_getgrsid_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
386 const char **domname, const char **name, gid_t *gid,
387 struct db_context **members)
389 struct wb_getgrsid_state *state = tevent_req_data(
390 req, struct wb_getgrsid_state);
391 NTSTATUS status;
393 D_INFO("WB command getgrsid end.\n");
394 if (tevent_req_is_nterror(req, &status)) {
395 D_WARNING("Failed with %s.\n", nt_errstr(status));
396 return status;
398 *domname = talloc_move(mem_ctx, &state->domname);
399 *name = talloc_move(mem_ctx, &state->name);
400 *gid = state->gid;
401 *members = talloc_move(mem_ctx, &state->members);
402 return NT_STATUS_OK;