r11423: Add some TALLOC_CTX
[jelmer/samba4-debian.git] / source / winbind / wb_samba3_cmd.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Main winbindd samba3 server routines
4
5    Copyright (C) Stefan Metzmacher      2005
6    Copyright (C) Volker Lendecke        2005
7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "smbd/service_stream.h"
26 #include "nsswitch/winbind_nss_config.h"
27 #include "nsswitch/winbindd_nss.h"
28 #include "winbind/wb_server.h"
29 #include "winbind/wb_samba3_protocol.h"
30 #include "winbind/wb_async_helpers.h"
31 #include "librpc/gen_ndr/nbt.h"
32 #include "libcli/raw/libcliraw.h"
33 #include "libcli/composite/composite.h"
34 #include "libcli/smb_composite/smb_composite.h"
35 #include "include/version.h"
36 #include "lib/events/events.h"
37 #include "librpc/gen_ndr/ndr_netlogon.h"
38
39 /* 
40    Send off the reply to an async Samba3 query, handling filling in the PAM, NTSTATUS and string errors.
41 */
42
43 static void wbsrv_samba3_async_auth_epilogue(NTSTATUS status,
44                                              struct wbsrv_samba3_call *s3call)
45 {
46         struct winbindd_response *resp = &s3call->response;
47         if (!NT_STATUS_IS_OK(status)) {
48                 resp->result = WINBINDD_ERROR;
49                 WBSRV_SAMBA3_SET_STRING(resp->data.auth.nt_status_string,
50                                         nt_errstr(status));
51                 WBSRV_SAMBA3_SET_STRING(resp->data.auth.error_string,
52                                         get_friendly_nt_error_msg(status));
53         } else {
54                 resp->result = WINBINDD_OK;
55         }
56
57         resp->data.auth.pam_error = nt_status_to_pam(status);
58         resp->data.auth.nt_status = NT_STATUS_V(status);
59
60         status = wbsrv_send_reply(s3call->call);
61         if (!NT_STATUS_IS_OK(status)) {
62                 wbsrv_terminate_connection(s3call->call->wbconn,
63                                            "wbsrv_queue_reply() failed");
64         }
65 }
66
67 /* 
68    Send of a generic reply to a Samba3 query
69 */
70
71 static void wbsrv_samba3_async_epilogue(NTSTATUS status,
72                                         struct wbsrv_samba3_call *s3call)
73 {
74         struct winbindd_response *resp = &s3call->response;
75         if (NT_STATUS_IS_OK(status)) {
76                 resp->result = WINBINDD_OK;
77         } else {
78                 resp->result = WINBINDD_ERROR;
79         }
80
81         status = wbsrv_send_reply(s3call->call);
82         if (!NT_STATUS_IS_OK(status)) {
83                 wbsrv_terminate_connection(s3call->call->wbconn,
84                                            "wbsrv_queue_reply() failed");
85         }
86 }
87
88 /* 
89    Boilerplate commands, simple queries without network traffic 
90 */
91
92 NTSTATUS wbsrv_samba3_interface_version(struct wbsrv_samba3_call *s3call)
93 {
94         s3call->response.result                 = WINBINDD_OK;
95         s3call->response.data.interface_version = WINBIND_INTERFACE_VERSION;
96         return NT_STATUS_OK;
97 }
98
99 NTSTATUS wbsrv_samba3_info(struct wbsrv_samba3_call *s3call)
100 {
101         s3call->response.result                 = WINBINDD_OK;
102         s3call->response.data.info.winbind_separator = *lp_winbind_separator();
103         WBSRV_SAMBA3_SET_STRING(s3call->response.data.info.samba_version,
104                                 SAMBA_VERSION_STRING);
105         return NT_STATUS_OK;
106 }
107
108 NTSTATUS wbsrv_samba3_domain_name(struct wbsrv_samba3_call *s3call)
109 {
110         s3call->response.result                 = WINBINDD_OK;
111         WBSRV_SAMBA3_SET_STRING(s3call->response.data.domain_name,
112                                 lp_workgroup());
113         return NT_STATUS_OK;
114 }
115
116 NTSTATUS wbsrv_samba3_netbios_name(struct wbsrv_samba3_call *s3call)
117 {
118         s3call->response.result                 = WINBINDD_OK;
119         WBSRV_SAMBA3_SET_STRING(s3call->response.data.netbios_name,
120                                 lp_netbios_name());
121         return NT_STATUS_OK;
122 }
123
124 NTSTATUS wbsrv_samba3_priv_pipe_dir(struct wbsrv_samba3_call *s3call)
125 {
126         s3call->response.result                 = WINBINDD_OK;
127         s3call->response.extra_data =
128                 smbd_tmp_path(s3call, WINBINDD_SAMBA3_PRIVILEGED_SOCKET);
129         NT_STATUS_HAVE_NO_MEMORY(s3call->response.extra_data);
130         return NT_STATUS_OK;
131 }
132
133 NTSTATUS wbsrv_samba3_ping(struct wbsrv_samba3_call *s3call)
134 {
135         s3call->response.result                 = WINBINDD_OK;
136         return NT_STATUS_OK;
137 }
138
139 /* 
140    Validate that we have a working pipe to the domain controller.
141    Return any NT error found in the process
142 */
143
144 static void checkmachacc_recv_creds(struct composite_context *ctx);
145
146 NTSTATUS wbsrv_samba3_check_machacc(struct wbsrv_samba3_call *s3call)
147 {
148         struct composite_context *ctx;
149
150         DEBUG(5, ("wbsrv_samba3_check_machacc called\n"));
151
152         ctx = wb_cmd_checkmachacc_send(s3call->call);
153         NT_STATUS_HAVE_NO_MEMORY(ctx);
154
155         ctx->async.fn = checkmachacc_recv_creds;
156         ctx->async.private_data = s3call;
157         s3call->call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
158         return NT_STATUS_OK;
159 }
160         
161 static void checkmachacc_recv_creds(struct composite_context *ctx)
162 {
163         struct wbsrv_samba3_call *s3call =
164                 talloc_get_type(ctx->async.private_data,
165                                 struct wbsrv_samba3_call);
166         NTSTATUS status;
167
168         status = wb_cmd_checkmachacc_recv(ctx);
169
170         wbsrv_samba3_async_auth_epilogue(status, s3call);
171 }
172
173 /*
174   Find the name of a suitable domain controller, by query on the
175   netlogon pipe to the DC.  
176 */
177
178 static void getdcname_recv_dc(struct composite_context *ctx);
179
180 NTSTATUS wbsrv_samba3_getdcname(struct wbsrv_samba3_call *s3call)
181 {
182         struct composite_context *ctx;
183         struct wbsrv_service *service =
184                 s3call->call->wbconn->listen_socket->service;
185
186         DEBUG(5, ("wbsrv_samba3_getdcname called\n"));
187
188         ctx = wb_cmd_getdcname_send(service, service->domains,
189                                     s3call->request.domain_name);
190         NT_STATUS_HAVE_NO_MEMORY(ctx);
191
192         ctx->async.fn = getdcname_recv_dc;
193         ctx->async.private_data = s3call;
194         s3call->call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
195         return NT_STATUS_OK;
196 }
197
198 static void getdcname_recv_dc(struct composite_context *ctx)
199 {
200         struct wbsrv_samba3_call *s3call =
201                 talloc_get_type(ctx->async.private_data,
202                                 struct wbsrv_samba3_call);
203         const char *dcname;
204         NTSTATUS status;
205
206         status = wb_cmd_getdcname_recv(ctx, s3call, &dcname);
207         if (!NT_STATUS_IS_OK(status)) goto done;
208
209         s3call->response.result = WINBINDD_OK;
210         WBSRV_SAMBA3_SET_STRING(s3call->response.data.dc_name, dcname);
211
212  done:
213         wbsrv_samba3_async_epilogue(status, s3call);
214 }
215
216 /* 
217    Lookup a user's domain groups
218 */
219
220 static void userdomgroups_recv_groups(struct composite_context *ctx);
221
222 NTSTATUS wbsrv_samba3_userdomgroups(struct wbsrv_samba3_call *s3call)
223 {
224         struct composite_context *ctx;
225         struct dom_sid *sid;
226
227         DEBUG(5, ("wbsrv_samba3_userdomgroups called\n"));
228
229         sid = dom_sid_parse_talloc(s3call, s3call->request.data.sid);
230         if (sid == NULL) {
231                 DEBUG(5, ("Could not parse sid %s\n",
232                           s3call->request.data.sid));
233                 return NT_STATUS_NO_MEMORY;
234         }
235
236         ctx = wb_cmd_userdomgroups_send(
237                 s3call->call->wbconn->listen_socket->service, sid);
238         NT_STATUS_HAVE_NO_MEMORY(ctx);
239
240         ctx->async.fn = userdomgroups_recv_groups;
241         ctx->async.private_data = s3call;
242         s3call->call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
243         return NT_STATUS_OK;
244 }
245
246 static void userdomgroups_recv_groups(struct composite_context *ctx)
247 {
248         struct wbsrv_samba3_call *s3call =
249                 talloc_get_type(ctx->async.private_data,
250                                 struct wbsrv_samba3_call);
251         int i, num_sids;
252         struct dom_sid **sids;
253         char *sids_string;
254         NTSTATUS status;
255
256         status = wb_cmd_userdomgroups_recv(ctx, s3call, &num_sids, &sids);
257         if (!NT_STATUS_IS_OK(status)) goto done;
258
259         sids_string = talloc_strdup(s3call, "");
260         if (sids_string == NULL) {
261                 status = NT_STATUS_NO_MEMORY;
262                 goto done;
263         }
264
265         for (i=0; i<num_sids; i++) {
266                 sids_string = talloc_asprintf_append(
267                         sids_string, "%s\n", dom_sid_string(s3call, sids[i]));
268         }
269
270         if (sids_string == NULL) {
271                 status = NT_STATUS_NO_MEMORY;
272                 goto done;
273         }
274
275         s3call->response.result = WINBINDD_OK;
276         s3call->response.extra_data = sids_string;
277         s3call->response.length += strlen(sids_string)+1;
278         s3call->response.data.num_entries = num_sids;
279
280  done:
281         wbsrv_samba3_async_epilogue(status, s3call);
282 }
283
284 /* 
285    Lookup the list of SIDs for a user 
286 */
287 static void usersids_recv_sids(struct composite_context *ctx);
288
289 NTSTATUS wbsrv_samba3_usersids(struct wbsrv_samba3_call *s3call)
290 {
291         struct composite_context *ctx;
292         struct dom_sid *sid;
293
294         DEBUG(5, ("wbsrv_samba3_usersids called\n"));
295
296         sid = dom_sid_parse_talloc(s3call, s3call->request.data.sid);
297         if (sid == NULL) {
298                 DEBUG(5, ("Could not parse sid %s\n",
299                           s3call->request.data.sid));
300                 return NT_STATUS_NO_MEMORY;
301         }
302
303         ctx = wb_cmd_usersids_send(
304                 s3call->call->wbconn->listen_socket->service, sid);
305         NT_STATUS_HAVE_NO_MEMORY(ctx);
306
307         ctx->async.fn = usersids_recv_sids;
308         ctx->async.private_data = s3call;
309         s3call->call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
310         return NT_STATUS_OK;
311 }
312
313 static void usersids_recv_sids(struct composite_context *ctx)
314 {
315         struct wbsrv_samba3_call *s3call =
316                 talloc_get_type(ctx->async.private_data,
317                                 struct wbsrv_samba3_call);
318         int i, num_sids;
319         struct dom_sid **sids;
320         char *sids_string;
321         NTSTATUS status;
322
323         status = wb_cmd_usersids_recv(ctx, s3call, &num_sids, &sids);
324         if (!NT_STATUS_IS_OK(status)) goto done;
325
326         sids_string = talloc_strdup(s3call, "");
327         if (sids_string == NULL) {
328                 status = NT_STATUS_NO_MEMORY;
329                 goto done;
330         }
331
332         for (i=0; i<num_sids; i++) {
333                 sids_string = talloc_asprintf_append(
334                         sids_string, "%s\n", dom_sid_string(s3call, sids[i]));
335                 if (sids_string == NULL) {
336                         status = NT_STATUS_NO_MEMORY;
337                         goto done;
338                 }
339         }
340
341         s3call->response.result = WINBINDD_OK;
342         s3call->response.extra_data = sids_string;
343         s3call->response.length += strlen(sids_string);
344         s3call->response.data.num_entries = num_sids;
345
346         /* Hmmmm. Nasty protocol -- who invented the zeros between the
347          * SIDs? Hmmm. Could have been me -- vl */
348
349         while (*sids_string != '\0') {
350                 if ((*sids_string) == '\n') {
351                         *sids_string = '\0';
352                 }
353                 sids_string += 1;
354         }
355
356  done:
357         wbsrv_samba3_async_epilogue(status, s3call);
358 }
359
360 /* 
361    Lookup a DOMAIN\\user style name, and return a SID
362 */
363
364 static void lookupname_recv_sid(struct composite_context *ctx);
365
366 NTSTATUS wbsrv_samba3_lookupname(struct wbsrv_samba3_call *s3call)
367 {
368         struct composite_context *ctx;
369         struct wbsrv_service *service =
370                 s3call->call->wbconn->listen_socket->service;
371
372         DEBUG(5, ("wbsrv_samba3_lookupname called\n"));
373
374         ctx = wb_cmd_lookupname_send(s3call, service,
375                                      s3call->request.data.name.dom_name,
376                                      s3call->request.data.name.name);
377         NT_STATUS_HAVE_NO_MEMORY(ctx);
378
379         /* setup the callbacks */
380         ctx->async.fn = lookupname_recv_sid;
381         ctx->async.private_data = s3call;
382         s3call->call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
383         return NT_STATUS_OK;
384 }
385
386 static void lookupname_recv_sid(struct composite_context *ctx)
387 {
388         struct wbsrv_samba3_call *s3call =
389                 talloc_get_type(ctx->async.private_data,
390                                 struct wbsrv_samba3_call);
391         struct wb_sid_object *sid;
392         NTSTATUS status;
393
394         status = wb_cmd_lookupname_recv(ctx, s3call, &sid);
395         if (!NT_STATUS_IS_OK(status)) goto done;
396
397         s3call->response.result = WINBINDD_OK;
398         s3call->response.data.sid.type = sid->type;
399         WBSRV_SAMBA3_SET_STRING(s3call->response.data.sid.sid,
400                                 dom_sid_string(s3call, sid->sid));
401
402  done:
403         wbsrv_samba3_async_epilogue(status, s3call);
404 }
405
406 /* 
407    Lookup a SID, and return a DOMAIN\\user style name
408 */
409
410 static void lookupsid_recv_name(struct composite_context *ctx);
411
412 NTSTATUS wbsrv_samba3_lookupsid(struct wbsrv_samba3_call *s3call)
413 {
414         struct composite_context *ctx;
415         struct wbsrv_service *service =
416                 s3call->call->wbconn->listen_socket->service;
417         struct dom_sid *sid;
418
419         DEBUG(5, ("wbsrv_samba3_lookupsid called\n"));
420
421         sid = dom_sid_parse_talloc(s3call, s3call->request.data.sid);
422         if (sid == NULL) {
423                 DEBUG(5, ("Could not parse sid %s\n",
424                           s3call->request.data.sid));
425                 return NT_STATUS_NO_MEMORY;
426         }
427
428         ctx = wb_cmd_lookupsid_send(s3call, service, sid);
429         NT_STATUS_HAVE_NO_MEMORY(ctx);
430
431         /* setup the callbacks */
432         ctx->async.fn = lookupsid_recv_name;
433         ctx->async.private_data = s3call;
434         s3call->call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
435         return NT_STATUS_OK;
436 }
437
438 static void lookupsid_recv_name(struct composite_context *ctx)
439 {
440         struct wbsrv_samba3_call *s3call =
441                 talloc_get_type(ctx->async.private_data,
442                                 struct wbsrv_samba3_call);
443         struct wb_sid_object *sid;
444         NTSTATUS status;
445
446         status = wb_cmd_lookupsid_recv(ctx, s3call, &sid);
447         if (!NT_STATUS_IS_OK(status)) goto done;
448
449         s3call->response.result = WINBINDD_OK;
450         s3call->response.data.name.type = sid->type;
451         WBSRV_SAMBA3_SET_STRING(s3call->response.data.name.dom_name,
452                                 sid->domain);
453         WBSRV_SAMBA3_SET_STRING(s3call->response.data.name.name, sid->name);
454
455  done:
456         wbsrv_samba3_async_epilogue(status, s3call);
457 }
458
459 /*
460   Challenge-response authentication.  This interface is used by
461   ntlm_auth and the smbd auth subsystem to pass NTLM authentication
462   requests along a common pipe to the domain controller.  
463
464   The return value (in the async reply) may include the 'info3'
465   (effectivly most things you would want to know about the user), or
466   the NT and LM session keys seperated.
467 */
468
469 static void pam_auth_crap_recv(struct composite_context *ctx);
470
471 NTSTATUS wbsrv_samba3_pam_auth_crap(struct wbsrv_samba3_call *s3call)
472 {
473         struct composite_context *ctx;
474         DATA_BLOB chal, nt_resp, lm_resp;
475
476         DEBUG(5, ("wbsrv_samba3_pam_auth_crap called\n"));
477
478         chal.data       = s3call->request.data.auth_crap.chal;
479         chal.length     = sizeof(s3call->request.data.auth_crap.chal);
480         nt_resp.data    = (uint8_t *)s3call->request.data.auth_crap.nt_resp;
481         nt_resp.length  = s3call->request.data.auth_crap.nt_resp_len;
482         lm_resp.data    = (uint8_t *)s3call->request.data.auth_crap.lm_resp;
483         lm_resp.length  = s3call->request.data.auth_crap.lm_resp_len;
484
485         ctx = wb_cmd_pam_auth_crap_send(
486                 s3call->call, 
487                 s3call->request.data.auth_crap.logon_parameters,
488                 s3call->request.data.auth_crap.domain,
489                 s3call->request.data.auth_crap.user,
490                 s3call->request.data.auth_crap.workstation,
491                 chal, nt_resp, lm_resp);
492         NT_STATUS_HAVE_NO_MEMORY(ctx);
493
494         ctx->async.fn = pam_auth_crap_recv;
495         ctx->async.private_data = s3call;
496         s3call->call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
497         return NT_STATUS_OK;
498 }
499
500 static void pam_auth_crap_recv(struct composite_context *ctx)
501 {
502         struct wbsrv_samba3_call *s3call =
503                 talloc_get_type(ctx->async.private_data,
504                                 struct wbsrv_samba3_call);
505         NTSTATUS status;
506         DATA_BLOB info3;
507         struct netr_UserSessionKey user_session_key;
508         struct netr_LMSessionKey lm_key;
509         char *unix_username;
510         
511         status = wb_cmd_pam_auth_crap_recv(ctx, s3call, &info3,
512                                            &user_session_key, &lm_key, &unix_username);
513         if (!NT_STATUS_IS_OK(status)) goto done;
514
515         if (s3call->request.flags & WBFLAG_PAM_USER_SESSION_KEY) {
516                 memcpy(s3call->response.data.auth.user_session_key, 
517                        &user_session_key.key,
518                        sizeof(s3call->response.data.auth.user_session_key));
519         }
520
521         if (s3call->request.flags & WBFLAG_PAM_INFO3_NDR) {
522                 s3call->response.extra_data = info3.data;
523                 s3call->response.length += info3.length;
524         }
525
526         if (s3call->request.flags & WBFLAG_PAM_LMKEY) {
527                 memcpy(s3call->response.data.auth.first_8_lm_hash, 
528                        lm_key.key,
529                        sizeof(s3call->response.data.auth.first_8_lm_hash));
530         }
531         
532         if (s3call->request.flags & WBFLAG_PAM_UNIX_NAME) {
533                 s3call->response.extra_data = unix_username;
534                 s3call->response.length += strlen(unix_username)+1;
535         }
536
537  done:
538         wbsrv_samba3_async_auth_epilogue(status, s3call);
539 }
540
541 /* Helper function: Split a domain\\user string into it's parts,
542  * because the client supplies it as one string */
543
544 static BOOL samba3_parse_domuser(TALLOC_CTX *mem_ctx, const char *domuser,
545                                  char **domain, char **user)
546 {
547         char *p = strchr(domuser, *lp_winbind_separator());
548
549         if (p == NULL) {
550                 *domain = talloc_strdup(mem_ctx, lp_workgroup());
551         } else {
552                 *domain = talloc_strndup(mem_ctx, domuser,
553                                          PTR_DIFF(p, domuser));
554                 domuser = p+1;
555         }
556
557         *user = talloc_strdup(mem_ctx, domuser);
558
559         return ((*domain != NULL) && (*user != NULL));
560 }
561
562 /* Plaintext authentication 
563    
564    This interface is used by ntlm_auth in it's 'basic' authentication
565    mode, as well as by pam_winbind to authenticate users where we are
566    given a plaintext password.
567 */
568
569 static void pam_auth_recv(struct composite_context *ctx);
570
571 NTSTATUS wbsrv_samba3_pam_auth(struct wbsrv_samba3_call *s3call)
572 {
573         struct composite_context *ctx;
574         char *user, *domain;
575         if (!samba3_parse_domuser(s3call, 
576                                  s3call->request.data.auth.user,
577                                  &domain, &user)) {
578                 return NT_STATUS_NO_SUCH_USER;
579         }
580
581         ctx = wb_cmd_pam_auth_send(
582                 s3call->call, domain, user,
583                 s3call->request.data.auth.pass);
584         NT_STATUS_HAVE_NO_MEMORY(ctx);
585
586         ctx->async.fn = pam_auth_recv;
587         ctx->async.private_data = s3call;
588         s3call->call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
589         return NT_STATUS_OK;
590 }
591
592 static void pam_auth_recv(struct composite_context *ctx)
593 {
594         struct wbsrv_samba3_call *s3call =
595                 talloc_get_type(ctx->async.private_data,
596                                 struct wbsrv_samba3_call);
597         NTSTATUS status;
598
599         status = wb_cmd_pam_auth_recv(ctx);
600
601         if (!NT_STATUS_IS_OK(status)) goto done;
602
603  done:
604         wbsrv_samba3_async_auth_epilogue(status, s3call);
605 }
606
607 /* 
608    List trusted domains
609 */
610
611 static void list_trustdom_recv_doms(struct composite_context *ctx);
612
613 NTSTATUS wbsrv_samba3_list_trustdom(struct wbsrv_samba3_call *s3call)
614 {
615         struct composite_context *ctx;
616         struct wbsrv_service *service =
617                 s3call->call->wbconn->listen_socket->service;
618
619         DEBUG(5, ("wbsrv_samba3_list_trustdom called\n"));
620
621         ctx = wb_cmd_list_trustdoms_send(service);
622         NT_STATUS_HAVE_NO_MEMORY(ctx);
623
624         ctx->async.fn = list_trustdom_recv_doms;
625         ctx->async.private_data = s3call;
626         s3call->call->flags |= WBSRV_CALL_FLAGS_REPLY_ASYNC;
627         return NT_STATUS_OK;
628 }
629
630 static void list_trustdom_recv_doms(struct composite_context *ctx)
631 {
632         struct wbsrv_samba3_call *s3call =
633                 talloc_get_type(ctx->async.private_data,
634                                 struct wbsrv_samba3_call);
635         int i, num_domains;
636         struct wb_dom_info **domains;
637         NTSTATUS status;
638         char *result;
639
640         status = wb_cmd_list_trustdoms_recv(ctx, s3call, &num_domains,
641                                             &domains);
642         if (!NT_STATUS_IS_OK(status)) goto done;
643
644         result = talloc_strdup(s3call, "");
645         if (result == NULL) {
646                 status = NT_STATUS_NO_MEMORY;
647                 goto done;
648         }
649
650         for (i=0; i<num_domains; i++) {
651                 result = talloc_asprintf_append(
652                         result, "%s\\%s\\%s",
653                         domains[i]->name, domains[i]->name,
654                         dom_sid_string(s3call, domains[i]->sid));
655         }
656
657         if (result == NULL) {
658                 status = NT_STATUS_NO_MEMORY;
659                 goto done;
660         }
661
662         s3call->response.result = WINBINDD_OK;
663         if (num_domains > 0) {
664                 s3call->response.extra_data = result;
665                 s3call->response.length += strlen(result)+1;
666         }
667
668  done:
669         wbsrv_samba3_async_epilogue(status, s3call);
670 }