1/* 2 Unix SMB/CIFS implementation. 3 4 Copyright (C) Rafal Szczesniak 2007 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 21#include "includes.h" 22#include "libnet/libnet.h" 23#include "libcli/composite/composite.h" 24#include "librpc/gen_ndr/lsa.h" 25#include "librpc/gen_ndr/ndr_lsa_c.h" 26#include "librpc/gen_ndr/samr.h" 27#include "librpc/gen_ndr/ndr_samr_c.h" 28#include "libcli/security/security.h" 29 30 31struct create_group_state { 32 struct libnet_context *ctx; 33 struct libnet_CreateGroup r; 34 struct libnet_DomainOpen domain_open; 35 struct libnet_rpc_groupadd group_add; 36 37 /* information about the progress */ 38 void (*monitor_fn)(struct monitor_msg *); 39}; 40 41 42static void continue_domain_opened(struct composite_context *ctx); 43static void continue_rpc_group_added(struct composite_context *ctx); 44 45 46struct composite_context* libnet_CreateGroup_send(struct libnet_context *ctx, 47 TALLOC_CTX *mem_ctx, 48 struct libnet_CreateGroup *r, 49 void (*monitor)(struct monitor_msg*)) 50{ 51 struct composite_context *c; 52 struct create_group_state *s; 53 struct composite_context *create_req; 54 bool prereq_met = false; 55 56 /* composite context allocation and setup */ 57 c = composite_create(mem_ctx, ctx->event_ctx); 58 if (c == NULL) return NULL; 59 60 s = talloc_zero(c, struct create_group_state); 61 if (composite_nomem(s, c)) return c; 62 63 c->private_data = s; 64 65 s->ctx = ctx; 66 s->r = *r; 67 ZERO_STRUCT(s->r.out); 68 69 /* prerequisite: make sure we have a valid samr domain handle */ 70 prereq_met = samr_domain_opened(ctx, s->r.in.domain_name, &c, &s->domain_open, 71 continue_domain_opened, monitor); 72 if (!prereq_met) return c; 73 74 /* prepare arguments of rpc group add call */ 75 s->group_add.in.groupname = r->in.group_name; 76 s->group_add.in.domain_handle = ctx->samr.handle; 77 78 /* send the request */ 79 create_req = libnet_rpc_groupadd_send(ctx->samr.pipe, &s->group_add, monitor); 80 if (composite_nomem(create_req, c)) return c; 81 82 composite_continue(c, create_req, continue_rpc_group_added, c); 83 return c; 84} 85 86 87static void continue_domain_opened(struct composite_context *ctx) 88{ 89 struct composite_context *c; 90 struct create_group_state *s; 91 struct composite_context *create_req; 92 93 c = talloc_get_type(ctx->async.private_data, struct composite_context); 94 s = talloc_get_type(c->private_data, struct create_group_state); 95 96 c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open); 97 if (!composite_is_ok(c)) return; 98 99 /* prepare arguments of groupadd call */ 100 s->group_add.in.groupname = s->r.in.group_name; 101 s->group_add.in.domain_handle = s->ctx->samr.handle; 102 103 /* send the request */ 104 create_req = libnet_rpc_groupadd_send(s->ctx->samr.pipe, &s->group_add, 105 s->monitor_fn); 106 if (composite_nomem(create_req, c)) return; 107 108 composite_continue(c, create_req, continue_rpc_group_added, c); 109} 110 111 112static void continue_rpc_group_added(struct composite_context *ctx) 113{ 114 struct composite_context *c; 115 struct create_group_state *s; 116 117 c = talloc_get_type(ctx->async.private_data, struct composite_context); 118 s = talloc_get_type(c->private_data, struct create_group_state); 119 120 /* receive result of group add call */ 121 c->status = libnet_rpc_groupadd_recv(ctx, c, &s->group_add); 122 if (!composite_is_ok(c)) return; 123 124 /* we're done */ 125 composite_done(c); 126} 127 128 129/** 130 * Receive result of CreateGroup call 131 * 132 * @param c composite context returned by send request routine 133 * @param mem_ctx memory context of this call 134 * @param r pointer to a structure containing arguments and result of this call 135 * @return nt status 136 */ 137NTSTATUS libnet_CreateGroup_recv(struct composite_context *c, 138 TALLOC_CTX *mem_ctx, 139 struct libnet_CreateGroup *r) 140{ 141 NTSTATUS status; 142 struct create_group_state *s; 143 144 status = composite_wait(c); 145 if (!NT_STATUS_IS_OK(status)) { 146 s = talloc_get_type(c->private_data, struct create_group_state); 147 r->out.error_string = talloc_strdup(mem_ctx, nt_errstr(status)); 148 } 149 150 return status; 151} 152 153 154/** 155 * Create domain group 156 * 157 * @param ctx initialised libnet context 158 * @param mem_ctx memory context of this call 159 * @param io pointer to structure containing arguments and result of this call 160 * @return nt status 161 */ 162NTSTATUS libnet_CreateGroup(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, 163 struct libnet_CreateGroup *io) 164{ 165 struct composite_context *c; 166 167 c = libnet_CreateGroup_send(ctx, mem_ctx, io, NULL); 168 return libnet_CreateGroup_recv(c, mem_ctx, io); 169} 170 171 172struct group_info_state { 173 struct libnet_context *ctx; 174 const char *domain_name; 175 enum libnet_GroupInfo_level level; 176 const char *group_name; 177 const char *sid_string; 178 struct libnet_LookupName lookup; 179 struct libnet_DomainOpen domopen; 180 struct libnet_rpc_groupinfo info; 181 182 /* information about the progress */ 183 void (*monitor_fn)(struct monitor_msg *); 184}; 185 186 187static void continue_domain_open_info(struct composite_context *ctx); 188static void continue_name_found(struct composite_context *ctx); 189static void continue_group_info(struct composite_context *ctx); 190 191/** 192 * Sends request to get group information 193 * 194 * @param ctx initialised libnet context 195 * @param mem_ctx memory context of this call 196 * @param io pointer to structure containing arguments the call 197 * @param monitor function pointer for receiving monitor messages 198 * @return composite context of this request 199 */ 200struct composite_context* libnet_GroupInfo_send(struct libnet_context *ctx, 201 TALLOC_CTX *mem_ctx, 202 struct libnet_GroupInfo *io, 203 void (*monitor)(struct monitor_msg*)) 204{ 205 struct composite_context *c; 206 struct group_info_state *s; 207 bool prereq_met = false; 208 struct composite_context *lookup_req, *info_req; 209 210 /* composite context allocation and setup */ 211 c = composite_create(mem_ctx, ctx->event_ctx); 212 if (c == NULL) return NULL; 213 214 s = talloc_zero(c, struct group_info_state); 215 if (composite_nomem(s, c)) return c; 216 217 c->private_data = s; 218 219 /* store arguments in the state structure */ 220 s->monitor_fn = monitor; 221 s->ctx = ctx; 222 s->domain_name = talloc_strdup(c, io->in.domain_name); 223 s->level = io->in.level; 224 switch(s->level) { 225 case GROUP_INFO_BY_NAME: 226 s->group_name = talloc_strdup(c, io->in.data.group_name); 227 s->sid_string = NULL; 228 break; 229 case GROUP_INFO_BY_SID: 230 s->group_name = NULL; 231 s->sid_string = dom_sid_string(c, io->in.data.group_sid); 232 break; 233 } 234 235 /* prerequisite: make sure the domain is opened */ 236 prereq_met = samr_domain_opened(ctx, s->domain_name, &c, &s->domopen, 237 continue_domain_open_info, monitor); 238 if (!prereq_met) return c; 239 240 switch(s->level) { 241 case GROUP_INFO_BY_NAME: 242 /* prepare arguments for LookupName call */ 243 s->lookup.in.name = s->group_name; 244 s->lookup.in.domain_name = s->domain_name; 245 246 /* send the request */ 247 lookup_req = libnet_LookupName_send(s->ctx, c, &s->lookup, s->monitor_fn); 248 if (composite_nomem(lookup_req, c)) return c; 249 250 /* set the next stage */ 251 composite_continue(c, lookup_req, continue_name_found, c); 252 break; 253 case GROUP_INFO_BY_SID: 254 /* prepare arguments for groupinfo call */ 255 s->info.in.domain_handle = s->ctx->samr.handle; 256 s->info.in.sid = s->sid_string; 257 /* we're looking for all information available */ 258 s->info.in.level = GROUPINFOALL; 259 260 /* send the request */ 261 info_req = libnet_rpc_groupinfo_send(s->ctx->samr.pipe, &s->info, s->monitor_fn); 262 if (composite_nomem(info_req, c)) return c; 263 264 /* set the next stage */ 265 composite_continue(c, info_req, continue_group_info, c); 266 break; 267 } 268 269 return c; 270} 271 272 273/* 274 * Stage 0.5 (optional): receive opened domain and send lookup name request 275 */ 276static void continue_domain_open_info(struct composite_context *ctx) 277{ 278 struct composite_context *c; 279 struct group_info_state *s; 280 struct composite_context *lookup_req, *info_req; 281 282 c = talloc_get_type(ctx->async.private_data, struct composite_context); 283 s = talloc_get_type(c->private_data, struct group_info_state); 284 285 /* receive domain handle */ 286 c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domopen); 287 if (!composite_is_ok(c)) return; 288 289 switch(s->level) { 290 case GROUP_INFO_BY_NAME: 291 /* prepare arguments for LookupName call */ 292 s->lookup.in.name = s->group_name; 293 s->lookup.in.domain_name = s->domain_name; 294 295 /* send the request */ 296 lookup_req = libnet_LookupName_send(s->ctx, c, &s->lookup, s->monitor_fn); 297 if (composite_nomem(lookup_req, c)) return; 298 299 /* set the next stage */ 300 composite_continue(c, lookup_req, continue_name_found, c); 301 break; 302 case GROUP_INFO_BY_SID: 303 /* prepare arguments for groupinfo call */ 304 s->info.in.domain_handle = s->ctx->samr.handle; 305 s->info.in.sid = s->sid_string; 306 /* we're looking for all information available */ 307 s->info.in.level = GROUPINFOALL; 308 309 /* send the request */ 310 info_req = libnet_rpc_groupinfo_send(s->ctx->samr.pipe, &s->info, s->monitor_fn); 311 if (composite_nomem(info_req, c)) return; 312 313 /* set the next stage */ 314 composite_continue(c, info_req, continue_group_info, c); 315 break; 316 317 } 318} 319 320 321/* 322 * Stage 1: Receive SID found and send request for group info 323 */ 324static void continue_name_found(struct composite_context *ctx) 325{ 326 struct composite_context *c; 327 struct group_info_state *s; 328 struct composite_context *info_req; 329 330 c = talloc_get_type(ctx->async.private_data, struct composite_context); 331 s = talloc_get_type(c->private_data, struct group_info_state); 332 333 /* receive SID assiociated with name found */ 334 c->status = libnet_LookupName_recv(ctx, c, &s->lookup); 335 if (!composite_is_ok(c)) return; 336 337 /* Is is a group SID actually ? */ 338 if (s->lookup.out.sid_type != SID_NAME_DOM_GRP && 339 s->lookup.out.sid_type != SID_NAME_ALIAS) { 340 composite_error(c, NT_STATUS_NO_SUCH_GROUP); 341 } 342 343 /* prepare arguments for groupinfo call */ 344 s->info.in.domain_handle = s->ctx->samr.handle; 345 s->info.in.groupname = s->group_name; 346 s->info.in.sid = s->lookup.out.sidstr; 347 /* we're looking for all information available */ 348 s->info.in.level = GROUPINFOALL; 349 350 /* send the request */ 351 info_req = libnet_rpc_groupinfo_send(s->ctx->samr.pipe, &s->info, s->monitor_fn); 352 if (composite_nomem(info_req, c)) return; 353 354 /* set the next stage */ 355 composite_continue(c, info_req, continue_group_info, c); 356} 357 358 359/* 360 * Stage 2: Receive group information 361 */ 362static void continue_group_info(struct composite_context *ctx) 363{ 364 struct composite_context *c; 365 struct group_info_state *s; 366 367 c = talloc_get_type(ctx->async.private_data, struct composite_context); 368 s = talloc_get_type(c->private_data, struct group_info_state); 369 370 /* receive group information */ 371 c->status = libnet_rpc_groupinfo_recv(ctx, c, &s->info); 372 if (!composite_is_ok(c)) return; 373 374 /* we're done */ 375 composite_done(c); 376} 377 378 379/* 380 * Receive group information 381 * 382 * @param c composite context returned by libnet_GroupInfo_send 383 * @param mem_ctx memory context of this call 384 * @param io pointer to structure receiving results of the call 385 * @result nt status 386 */ 387NTSTATUS libnet_GroupInfo_recv(struct composite_context* c, TALLOC_CTX *mem_ctx, 388 struct libnet_GroupInfo *io) 389{ 390 NTSTATUS status; 391 struct group_info_state *s; 392 393 status = composite_wait(c); 394 if (NT_STATUS_IS_OK(status)) { 395 /* put the results into io structure if everything went fine */ 396 s = talloc_get_type(c->private_data, struct group_info_state); 397 398 io->out.group_name = talloc_steal(mem_ctx, 399 s->info.out.info.all.name.string); 400 io->out.group_sid = talloc_steal(mem_ctx, s->lookup.out.sid); 401 io->out.num_members = s->info.out.info.all.num_members; 402 io->out.description = talloc_steal(mem_ctx, s->info.out.info.all.description.string); 403 404 io->out.error_string = talloc_strdup(mem_ctx, "Success"); 405 406 } else { 407 io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status)); 408 } 409 410 talloc_free(c); 411 412 return status; 413} 414 415 416/** 417 * Obtains specified group information 418 * 419 * @param ctx initialised libnet context 420 * @param mem_ctx memory context of the call 421 * @param io pointer to a structure containing arguments and results of the call 422 */ 423NTSTATUS libnet_GroupInfo(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, 424 struct libnet_GroupInfo *io) 425{ 426 struct composite_context *c = libnet_GroupInfo_send(ctx, mem_ctx, 427 io, NULL); 428 return libnet_GroupInfo_recv(c, mem_ctx, io); 429} 430 431 432struct grouplist_state { 433 struct libnet_context *ctx; 434 const char *domain_name; 435 struct lsa_DomainInfo dominfo; 436 int page_size; 437 uint32_t resume_index; 438 struct grouplist *groups; 439 uint32_t count; 440 441 struct libnet_DomainOpen domain_open; 442 struct lsa_QueryInfoPolicy query_domain; 443 struct samr_EnumDomainGroups group_list; 444 445 void (*monitor_fn)(struct monitor_msg*); 446}; 447 448 449static void continue_lsa_domain_opened(struct composite_context *ctx); 450static void continue_domain_queried(struct rpc_request *req); 451static void continue_samr_domain_opened(struct composite_context *ctx); 452static void continue_domain_queried(struct rpc_request *req); 453static void continue_groups_enumerated(struct rpc_request *req); 454 455 456/** 457 * Sends request to list (enumerate) group accounts 458 * 459 * @param ctx initialised libnet context 460 * @param mem_ctx memory context of this call 461 * @param io pointer to structure containing arguments and results of this call 462 * @param monitor function pointer for receiving monitor messages 463 * @return compostite context of this request 464 */ 465struct composite_context *libnet_GroupList_send(struct libnet_context *ctx, 466 TALLOC_CTX *mem_ctx, 467 struct libnet_GroupList *io, 468 void (*monitor)(struct monitor_msg*)) 469{ 470 struct composite_context *c; 471 struct grouplist_state *s; 472 struct rpc_request *query_req; 473 bool prereq_met = false; 474 475 /* composite context allocation and setup */ 476 c = composite_create(mem_ctx, ctx->event_ctx); 477 if (c == NULL) return NULL; 478 479 s = talloc_zero(c, struct grouplist_state); 480 if (composite_nomem(s, c)) return c; 481 482 c->private_data = s; 483 484 /* store the arguments in the state structure */ 485 s->ctx = ctx; 486 s->page_size = io->in.page_size; 487 s->resume_index = io->in.resume_index; 488 s->domain_name = talloc_strdup(c, io->in.domain_name); 489 s->monitor_fn = monitor; 490 491 /* make sure we have lsa domain handle before doing anything */ 492 prereq_met = lsa_domain_opened(ctx, s->domain_name, &c, &s->domain_open, 493 continue_lsa_domain_opened, monitor); 494 if (!prereq_met) return c; 495 496 /* prepare arguments of QueryDomainInfo call */ 497 s->query_domain.in.handle = &ctx->lsa.handle; 498 s->query_domain.in.level = LSA_POLICY_INFO_DOMAIN; 499 s->query_domain.out.info = talloc_zero(c, union lsa_PolicyInformation *); 500 if (composite_nomem(s->query_domain.out.info, c)) return c; 501 502 /* send the request */ 503 query_req = dcerpc_lsa_QueryInfoPolicy_send(ctx->lsa.pipe, c, &s->query_domain); 504 if (composite_nomem(query_req, c)) return c; 505 506 composite_continue_rpc(c, query_req, continue_domain_queried, c); 507 return c; 508} 509 510 511/* 512 * Stage 0.5 (optional): receive lsa domain handle and send 513 * request to query domain info 514 */ 515static void continue_lsa_domain_opened(struct composite_context *ctx) 516{ 517 struct composite_context *c; 518 struct grouplist_state *s; 519 struct rpc_request *query_req; 520 521 c = talloc_get_type(ctx->async.private_data, struct composite_context); 522 s = talloc_get_type(c->private_data, struct grouplist_state); 523 524 /* receive lsa domain handle */ 525 c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open); 526 if (!composite_is_ok(c)) return; 527 528 /* prepare arguments of QueryDomainInfo call */ 529 s->query_domain.in.handle = &s->ctx->lsa.handle; 530 s->query_domain.in.level = LSA_POLICY_INFO_DOMAIN; 531 s->query_domain.out.info = talloc_zero(c, union lsa_PolicyInformation *); 532 if (composite_nomem(s->query_domain.out.info, c)) return; 533 534 /* send the request */ 535 query_req = dcerpc_lsa_QueryInfoPolicy_send(s->ctx->lsa.pipe, c, &s->query_domain); 536 if (composite_nomem(query_req, c)) return; 537 538 composite_continue_rpc(c, query_req, continue_domain_queried, c); 539} 540 541 542/* 543 * Stage 1: receive domain info and request to enum groups 544 * provided a valid samr handle is opened 545 */ 546static void continue_domain_queried(struct rpc_request *req) 547{ 548 struct composite_context *c; 549 struct grouplist_state *s; 550 struct rpc_request *enum_req; 551 bool prereq_met = false; 552 553 c = talloc_get_type(req->async.private_data, struct composite_context); 554 s = talloc_get_type(c->private_data, struct grouplist_state); 555 556 /* receive result of rpc request */ 557 c->status = dcerpc_ndr_request_recv(req); 558 if (!composite_is_ok(c)) return; 559 560 /* get the returned domain info */ 561 s->dominfo = (*s->query_domain.out.info)->domain; 562 563 /* make sure we have samr domain handle before continuing */ 564 prereq_met = samr_domain_opened(s->ctx, s->domain_name, &c, &s->domain_open, 565 continue_samr_domain_opened, s->monitor_fn); 566 if (!prereq_met) return; 567 568 /* prepare arguments od EnumDomainGroups call */ 569 s->group_list.in.domain_handle = &s->ctx->samr.handle; 570 s->group_list.in.max_size = s->page_size; 571 s->group_list.in.resume_handle = &s->resume_index; 572 s->group_list.out.resume_handle = &s->resume_index; 573 s->group_list.out.num_entries = talloc(s, uint32_t); 574 if (composite_nomem(s->group_list.out.num_entries, c)) return; 575 s->group_list.out.sam = talloc(s, struct samr_SamArray *); 576 if (composite_nomem(s->group_list.out.sam, c)) return; 577 578 /* send the request */ 579 enum_req = dcerpc_samr_EnumDomainGroups_send(s->ctx->samr.pipe, c, &s->group_list); 580 if (composite_nomem(enum_req, c)) return; 581 582 composite_continue_rpc(c, enum_req, continue_groups_enumerated, c); 583} 584 585 586/* 587 * Stage 1.5 (optional): receive samr domain handle 588 * and request to enumerate accounts 589 */ 590static void continue_samr_domain_opened(struct composite_context *ctx) 591{ 592 struct composite_context *c; 593 struct grouplist_state *s; 594 struct rpc_request *enum_req; 595 596 c = talloc_get_type(ctx->async.private_data, struct composite_context); 597 s = talloc_get_type(c->private_data, struct grouplist_state); 598 599 /* receive samr domain handle */ 600 c->status = libnet_DomainOpen_recv(ctx, s->ctx, c, &s->domain_open); 601 if (!composite_is_ok(c)) return; 602 603 /* prepare arguments of EnumDomainGroups call */ 604 s->group_list.in.domain_handle = &s->ctx->samr.handle; 605 s->group_list.in.max_size = s->page_size; 606 s->group_list.in.resume_handle = &s->resume_index; 607 s->group_list.out.resume_handle = &s->resume_index; 608 s->group_list.out.num_entries = talloc(s, uint32_t); 609 if (composite_nomem(s->group_list.out.num_entries, c)) return; 610 s->group_list.out.sam = talloc(s, struct samr_SamArray *); 611 if (composite_nomem(s->group_list.out.sam, c)) return; 612 613 /* send the request */ 614 enum_req = dcerpc_samr_EnumDomainGroups_send(s->ctx->samr.pipe, c, &s->group_list); 615 if (composite_nomem(enum_req, c)) return; 616 617 composite_continue_rpc(c, enum_req, continue_groups_enumerated, c); 618} 619 620 621/* 622 * Stage 2: receive enumerated groups and their rids 623 */ 624static void continue_groups_enumerated(struct rpc_request *req) 625{ 626 struct composite_context *c; 627 struct grouplist_state *s; 628 int i; 629 630 c = talloc_get_type(req->async.private_data, struct composite_context); 631 s = talloc_get_type(c->private_data, struct grouplist_state); 632 633 /* receive result of rpc request */ 634 c->status = dcerpc_ndr_request_recv(req); 635 if (!composite_is_ok(c)) return; 636 637 /* get the actual status of the rpc call result 638 (instead of rpc layer) */ 639 c->status = s->group_list.out.result; 640 641 /* we're interested in status "ok" as well as two 642 enum-specific status codes */ 643 if (NT_STATUS_IS_OK(c->status) || 644 NT_STATUS_EQUAL(c->status, STATUS_MORE_ENTRIES) || 645 NT_STATUS_EQUAL(c->status, NT_STATUS_NO_MORE_ENTRIES)) { 646 647 /* get enumerated accounts counter and resume handle (the latter allows 648 making subsequent call to continue enumeration) */ 649 s->resume_index = *s->group_list.out.resume_handle; 650 s->count = *s->group_list.out.num_entries; 651 652 /* prepare returned group accounts array */ 653 s->groups = talloc_array(c, struct grouplist, (*s->group_list.out.sam)->count); 654 if (composite_nomem(s->groups, c)) return; 655 656 for (i = 0; i < (*s->group_list.out.sam)->count; i++) { 657 struct dom_sid *group_sid; 658 struct samr_SamEntry *entry = &(*s->group_list.out.sam)->entries[i]; 659 struct dom_sid *domain_sid = (*s->query_domain.out.info)->domain.sid; 660 661 /* construct group sid from returned rid and queried domain sid */ 662 group_sid = dom_sid_add_rid(c, domain_sid, entry->idx); 663 if (composite_nomem(group_sid, c)) return; 664 665 /* groupname */ 666 s->groups[i].groupname = talloc_strdup(c, entry->name.string); 667 if (composite_nomem(s->groups[i].groupname, c)) return; 668 669 /* sid string */ 670 s->groups[i].sid = dom_sid_string(c, group_sid); 671 if (composite_nomem(s->groups[i].sid, c)) return; 672 } 673 674 /* that's it */ 675 composite_done(c); 676 677 } else { 678 /* something went wrong */ 679 composite_error(c, c->status); 680 } 681} 682 683 684/** 685 * Receive result of GroupList call 686 * 687 * @param c composite context returned by send request routine 688 * @param mem_ctx memory context of this call 689 * @param io pointer to structure containing arguments and result of this call 690 * @param nt status 691 */ 692NTSTATUS libnet_GroupList_recv(struct composite_context *c, TALLOC_CTX *mem_ctx, 693 struct libnet_GroupList *io) 694{ 695 NTSTATUS status; 696 struct grouplist_state *s; 697 698 if (c == NULL || mem_ctx == NULL || io == NULL) { 699 return NT_STATUS_INVALID_PARAMETER; 700 } 701 702 status = composite_wait(c); 703 if (NT_STATUS_IS_OK(status) || 704 NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) || 705 NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) { 706 707 s = talloc_get_type(c->private_data, struct grouplist_state); 708 709 /* get results from composite context */ 710 io->out.count = s->count; 711 io->out.resume_index = s->resume_index; 712 io->out.groups = talloc_steal(mem_ctx, s->groups); 713 714 if (NT_STATUS_IS_OK(status)) { 715 io->out.error_string = talloc_asprintf(mem_ctx, "Success"); 716 } else { 717 /* success, but we're not done yet */ 718 io->out.error_string = talloc_asprintf(mem_ctx, "Success (status: %s)", 719 nt_errstr(status)); 720 } 721 722 } else { 723 io->out.error_string = talloc_asprintf(mem_ctx, "Error: %s", nt_errstr(status)); 724 } 725 726 return status; 727} 728 729 730/** 731 * Enumerate domain groups 732 * 733 * @param ctx initialised libnet context 734 * @param mem_ctx memory context of this call 735 * @param io pointer to structure containing arguments and result of this call 736 * @return nt status 737 */ 738NTSTATUS libnet_GroupList(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, 739 struct libnet_GroupList *io) 740{ 741 struct composite_context *c; 742 743 c = libnet_GroupList_send(ctx, mem_ctx, io, NULL); 744 return libnet_GroupList_recv(c, mem_ctx, io); 745} 746