1/*
2   Unix SMB/CIFS implementation.
3   async implementation of WINBINDD_LIST_GROUPS
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
24struct winbindd_list_groups_domstate {
25	struct tevent_req *subreq;
26	struct winbindd_domain *domain;
27	struct wbint_Principals groups;
28};
29
30struct winbindd_list_groups_state {
31	int num_received;
32	/* All domains */
33	int num_domains;
34	struct winbindd_list_groups_domstate *domains;
35};
36
37static void winbindd_list_groups_done(struct tevent_req *subreq);
38
39struct tevent_req *winbindd_list_groups_send(TALLOC_CTX *mem_ctx,
40					     struct tevent_context *ev,
41					     struct winbindd_cli_state *cli,
42					     struct winbindd_request *request)
43{
44	struct tevent_req *req;
45	struct winbindd_list_groups_state *state;
46	struct winbindd_domain *domain;
47	int i;
48
49	req = tevent_req_create(mem_ctx, &state,
50				struct winbindd_list_groups_state);
51	if (req == NULL) {
52		return NULL;
53	}
54
55	/* Ensure null termination */
56	request->domain_name[sizeof(request->domain_name)-1]='\0';
57
58	DEBUG(3, ("list_groups %s\n", request->domain_name));
59
60	if (request->domain_name[0] != '\0') {
61		state->num_domains = 1;
62	} else {
63		state->num_domains = 0;
64		for (domain = domain_list(); domain; domain = domain->next) {
65			state->num_domains += 1;
66		}
67	}
68
69	state->domains = talloc_array(state,
70				      struct winbindd_list_groups_domstate,
71				      state->num_domains);
72	if (tevent_req_nomem(state->domains, req)) {
73		return tevent_req_post(req, ev);
74	}
75
76	if (request->domain_name[0] != '\0') {
77		state->domains[0].domain = find_domain_from_name_noinit(
78			request->domain_name);
79		if (state->domains[0].domain == NULL) {
80			tevent_req_nterror(req, NT_STATUS_NO_SUCH_DOMAIN);
81			return tevent_req_post(req, ev);
82		}
83	} else {
84		i = 0;
85		for (domain = domain_list(); domain; domain = domain->next) {
86			state->domains[i++].domain = domain;
87		}
88	}
89
90	for (i=0; i<state->num_domains; i++) {
91		struct winbindd_list_groups_domstate *d = &state->domains[i];
92
93		d->subreq = rpccli_wbint_QueryGroupList_send(
94			state->domains, ev, d->domain->child.rpccli,
95			&d->groups);
96		if (tevent_req_nomem(d->subreq, req)) {
97			TALLOC_FREE(state->domains);
98			return tevent_req_post(req, ev);
99		}
100		tevent_req_set_callback(d->subreq, winbindd_list_groups_done,
101					req);
102	}
103	state->num_received = 0;
104	return req;
105}
106
107static void winbindd_list_groups_done(struct tevent_req *subreq)
108{
109	struct tevent_req *req = tevent_req_callback_data(
110		subreq, struct tevent_req);
111	struct winbindd_list_groups_state *state = tevent_req_data(
112		req, struct winbindd_list_groups_state);
113	NTSTATUS status, result;
114	int i;
115
116	status = rpccli_wbint_QueryGroupList_recv(subreq, state->domains,
117						  &result);
118
119	for (i=0; i<state->num_domains; i++) {
120		if (subreq == state->domains[i].subreq) {
121			break;
122		}
123	}
124	if (i < state->num_domains) {
125		struct winbindd_list_groups_domstate *d = &state->domains[i];
126
127		DEBUG(10, ("Domain %s returned %d users\n", d->domain->name,
128			   d->groups.num_principals));
129
130		d->subreq = NULL;
131
132		if (!NT_STATUS_IS_OK(status) || !NT_STATUS_IS_OK(result)) {
133			DEBUG(10, ("list_groups for domain %s failed\n",
134				   d->domain->name));
135			d->groups.num_principals = 0;
136		}
137	}
138
139	TALLOC_FREE(subreq);
140
141	state->num_received += 1;
142
143	if (state->num_received >= state->num_domains) {
144		tevent_req_done(req);
145	}
146}
147
148NTSTATUS winbindd_list_groups_recv(struct tevent_req *req,
149				   struct winbindd_response *response)
150{
151	struct winbindd_list_groups_state *state = tevent_req_data(
152		req, struct winbindd_list_groups_state);
153	NTSTATUS status;
154	char *result;
155	int i;
156	uint32_t j;
157	size_t len;
158
159	if (tevent_req_is_nterror(req, &status)) {
160		return status;
161	}
162
163	len = 0;
164	for (i=0; i<state->num_domains; i++) {
165		struct winbindd_list_groups_domstate *d = &state->domains[i];
166
167		for (j=0; j<d->groups.num_principals; j++) {
168			fstring name;
169			fill_domain_username(name, d->domain->name,
170					     d->groups.principals[j].name,
171					     True);
172			len += strlen(name)+1;
173		}
174	}
175
176	result = talloc_array(response, char, len+1);
177	if (result == 0) {
178		return NT_STATUS_NO_MEMORY;
179	}
180
181	len = 0;
182	for (i=0; i<state->num_domains; i++) {
183		struct winbindd_list_groups_domstate *d = &state->domains[i];
184
185		for (j=0; j<d->groups.num_principals; j++) {
186			fstring name;
187			size_t this_len;
188			fill_domain_username(name, d->domain->name,
189					     d->groups.principals[j].name,
190					     True);
191			this_len = strlen(name);
192			memcpy(result+len, name, this_len);
193			len += this_len;
194			result[len] = ',';
195			len += 1;
196		}
197	}
198	result[len-1] = '\0';
199
200	response->extra_data.data = result;
201	response->length += len;
202
203	return NT_STATUS_OK;
204}
205