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