lib: Remove timeval_set()
[samba.git] / source3 / winbindd / winbindd_list_groups.c
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 "util/debug.h"
22 #include "winbindd.h"
23 #include "librpc/gen_ndr/ndr_winbind_c.h"
24
25 struct winbindd_list_groups_domstate {
26         struct tevent_req *subreq;
27         struct winbindd_domain *domain;
28         struct wbint_Principals groups;
29 };
30
31 struct winbindd_list_groups_state {
32         uint32_t num_received;
33         /* All domains */
34         uint32_t num_domains;
35         struct winbindd_list_groups_domstate *domains;
36 };
37
38 static void winbindd_list_groups_done(struct tevent_req *subreq);
39
40 struct tevent_req *winbindd_list_groups_send(TALLOC_CTX *mem_ctx,
41                                              struct tevent_context *ev,
42                                              struct winbindd_cli_state *cli,
43                                              struct winbindd_request *request)
44 {
45         struct tevent_req *req;
46         struct winbindd_list_groups_state *state;
47         struct winbindd_domain *domain;
48         uint32_t i;
49
50         req = tevent_req_create(mem_ctx, &state,
51                                 struct winbindd_list_groups_state);
52         if (req == NULL) {
53                 return NULL;
54         }
55
56         D_NOTICE("[%s (%u)] Winbind external command LIST_GROUPS start.\n"
57                  "WBFLAG_FROM_NSS is %s, winbind enum groups is %d.\n",
58                  cli->client_name,
59                  (unsigned int)cli->pid,
60                  request->wb_flags & WBFLAG_FROM_NSS ? "Set" : "Unset",
61                  lp_winbind_enum_groups());
62
63         if (request->wb_flags & WBFLAG_FROM_NSS && !lp_winbind_enum_groups()) {
64                 tevent_req_done(req);
65                 return tevent_req_post(req, ev);
66         }
67
68         /* Ensure null termination */
69         request->domain_name[sizeof(request->domain_name)-1]='\0';
70
71         if (request->domain_name[0] != '\0') {
72                 state->num_domains = 1;
73                 D_DEBUG("List groups for domain %s.\n", request->domain_name);
74         } else {
75                 state->num_domains = 0;
76                 for (domain = domain_list(); domain; domain = domain->next) {
77                         state->num_domains += 1;
78                 }
79                 D_DEBUG("List groups for %"PRIu32" domain(s).\n", state->num_domains);
80         }
81
82         state->domains = talloc_array(state,
83                                       struct winbindd_list_groups_domstate,
84                                       state->num_domains);
85         if (tevent_req_nomem(state->domains, req)) {
86                 return tevent_req_post(req, ev);
87         }
88
89         if (request->domain_name[0] != '\0') {
90                 ZERO_STRUCT(state->domains[0].groups);
91
92                 state->domains[0].domain = find_domain_from_name_noinit(
93                         request->domain_name);
94                 if (state->domains[0].domain == NULL) {
95                         tevent_req_nterror(req, NT_STATUS_NO_SUCH_DOMAIN);
96                         return tevent_req_post(req, ev);
97                 }
98         } else {
99                 i = 0;
100                 for (domain = domain_list(); domain; domain = domain->next) {
101                         ZERO_STRUCT(state->domains[i].groups);
102
103                         state->domains[i].domain = domain;
104                         i++;
105                 }
106         }
107
108         for (i=0; i<state->num_domains; i++) {
109                 struct winbindd_list_groups_domstate *d = &state->domains[i];
110
111                 d->subreq = dcerpc_wbint_QueryGroupList_send(
112                         state->domains, ev, dom_child_handle(d->domain),
113                         &d->groups);
114                 if (tevent_req_nomem(d->subreq, req)) {
115                         TALLOC_FREE(state->domains);
116                         return tevent_req_post(req, ev);
117                 }
118                 tevent_req_set_callback(d->subreq, winbindd_list_groups_done,
119                                         req);
120         }
121         state->num_received = 0;
122         return req;
123 }
124
125 static void winbindd_list_groups_done(struct tevent_req *subreq)
126 {
127         struct tevent_req *req = tevent_req_callback_data(
128                 subreq, struct tevent_req);
129         struct winbindd_list_groups_state *state = tevent_req_data(
130                 req, struct winbindd_list_groups_state);
131         NTSTATUS status, result;
132         uint32_t i;
133
134         status = dcerpc_wbint_QueryGroupList_recv(subreq, state->domains,
135                                                   &result);
136
137         for (i=0; i<state->num_domains; i++) {
138                 if (subreq == state->domains[i].subreq) {
139                         break;
140                 }
141         }
142         if (i < state->num_domains) {
143                 struct winbindd_list_groups_domstate *d = &state->domains[i];
144
145                 D_DEBUG("Domain %s returned %"PRIu32" groups\n", d->domain->name,
146                            d->groups.num_principals);
147
148                 d->subreq = NULL;
149
150                 if (!NT_STATUS_IS_OK(status) || !NT_STATUS_IS_OK(result)) {
151                         D_WARNING("list_groups for domain %s failed\n",
152                                    d->domain->name);
153                         d->groups.num_principals = 0;
154                 }
155         }
156
157         TALLOC_FREE(subreq);
158
159         state->num_received += 1;
160
161         if (state->num_received >= state->num_domains) {
162                 tevent_req_done(req);
163         }
164 }
165
166 NTSTATUS winbindd_list_groups_recv(struct tevent_req *req,
167                                    struct winbindd_response *response)
168 {
169         struct winbindd_list_groups_state *state = tevent_req_data(
170                 req, struct winbindd_list_groups_state);
171         NTSTATUS status;
172         char *result;
173         uint32_t i, j, num_entries = 0;
174         size_t len;
175
176         D_NOTICE("Winbind external command LIST_GROUPS end.\n");
177         if (tevent_req_is_nterror(req, &status)) {
178                 D_WARNING("Failed with %s.\n", nt_errstr(status));
179                 return status;
180         }
181
182         len = 0;
183         response->data.num_entries = 0;
184         for (i=0; i<state->num_domains; i++) {
185                 struct winbindd_list_groups_domstate *d = &state->domains[i];
186
187                 for (j=0; j<d->groups.num_principals; j++) {
188                         const char *name;
189                         name = fill_domain_username_talloc(response, d->domain->name,
190                                              d->groups.principals[j].name,
191                                              True);
192                         if (name == NULL) {
193                                 return NT_STATUS_NO_MEMORY;
194                         }
195                         len += strlen(name)+1;
196                 }
197                 response->data.num_entries += d->groups.num_principals;
198         }
199
200         result = talloc_array(response, char, len+1);
201         if (result == 0) {
202                 return NT_STATUS_NO_MEMORY;
203         }
204
205         len = 0;
206         for (i=0; i<state->num_domains; i++) {
207                 struct winbindd_list_groups_domstate *d = &state->domains[i];
208
209                 for (j=0; j<d->groups.num_principals; j++) {
210                         const char *name;
211                         size_t this_len;
212                         name = fill_domain_username_talloc(response, d->domain->name,
213                                              d->groups.principals[j].name,
214                                              True);
215                         if (name == NULL) {
216                                 return NT_STATUS_NO_MEMORY;
217                         }
218                         this_len = strlen(name);
219                         memcpy(result+len, name, this_len);
220                         len += this_len;
221                         result[len] = ',';
222                         len += 1;
223                         num_entries++;
224                 }
225         }
226         result[len-1] = '\0';
227
228         response->data.num_entries = num_entries;
229         response->extra_data.data = result;
230         response->length += len;
231
232         return NT_STATUS_OK;
233 }