• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/samba-3.5.8/source4/libnet/
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