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 44struct wb_lookupgroupmem_state { 45 struct dom_sid sid; 46 struct wbint_Principals members; 47}; 48 49static void wb_lookupgroupmem_done(struct tevent_req *subreq); 50 51static 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 83static 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 104static 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 126struct 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 134static NTSTATUS wb_groups_members_next_subreq( 135 struct wb_groups_members_state *state, 136 TALLOC_CTX *mem_ctx, struct tevent_req **psubreq); 137static void wb_groups_members_done(struct tevent_req *subreq); 138 139static 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 172static 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 195static 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 251static 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 275struct wb_group_members_state { 276 struct tevent_context *ev; 277 int depth; 278 struct talloc_dict *users; 279 struct wbint_Principal *groups; 280}; 281 282static NTSTATUS wb_group_members_next_subreq( 283 struct wb_group_members_state *state, 284 TALLOC_CTX *mem_ctx, struct tevent_req **psubreq); 285static void wb_group_members_done(struct tevent_req *subreq); 286 287struct 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 330static 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 353static 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 456NTSTATUS 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} 469