s3-talloc Change TALLOC_REALLOC_ARRAY() to talloc_realloc()
[ira/wip.git] / source3 / winbindd / winbindd_dual_srv.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    In-Child server implementation of the routines defined in wbint.idl
5
6    Copyright (C) Volker Lendecke 2009
7    Copyright (C) Guenther Deschner 2009
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 3 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, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "winbindd/winbindd.h"
25 #include "winbindd/winbindd_proto.h"
26 #include "rpc_client/cli_pipe.h"
27 #include "ntdomain.h"
28 #include "librpc/gen_ndr/srv_wbint.h"
29 #include "../librpc/gen_ndr/ndr_netlogon_c.h"
30 #include "idmap.h"
31 #include "../libcli/security/security.h"
32
33 void _wbint_Ping(struct pipes_struct *p, struct wbint_Ping *r)
34 {
35         *r->out.out_data = r->in.in_data;
36 }
37
38 NTSTATUS _wbint_LookupSid(struct pipes_struct *p, struct wbint_LookupSid *r)
39 {
40         struct winbindd_domain *domain = wb_child_domain();
41         char *dom_name;
42         char *name;
43         enum lsa_SidType type;
44         NTSTATUS status;
45
46         if (domain == NULL) {
47                 return NT_STATUS_REQUEST_NOT_ACCEPTED;
48         }
49
50         status = domain->methods->sid_to_name(domain, p->mem_ctx, r->in.sid,
51                                               &dom_name, &name, &type);
52         if (!NT_STATUS_IS_OK(status)) {
53                 return status;
54         }
55
56         *r->out.domain = dom_name;
57         *r->out.name = name;
58         *r->out.type = type;
59         return NT_STATUS_OK;
60 }
61
62 NTSTATUS _wbint_LookupSids(struct pipes_struct *p, struct wbint_LookupSids *r)
63 {
64         struct winbindd_domain *domain = wb_child_domain();
65
66         if (domain == NULL) {
67                 return NT_STATUS_REQUEST_NOT_ACCEPTED;
68         }
69
70         /*
71          * This breaks the winbindd_domain->methods abstraction: This
72          * is only called for remote domains, and both winbindd_msrpc
73          * and winbindd_ad call into lsa_lookupsids anyway. Caching is
74          * done at the wbint RPC layer.
75          */
76         return rpc_lookup_sids(p->mem_ctx, domain, r->in.sids,
77                                &r->out.domains, &r->out.names);
78 }
79
80 NTSTATUS _wbint_LookupName(struct pipes_struct *p, struct wbint_LookupName *r)
81 {
82         struct winbindd_domain *domain = wb_child_domain();
83
84         if (domain == NULL) {
85                 return NT_STATUS_REQUEST_NOT_ACCEPTED;
86         }
87
88         return domain->methods->name_to_sid(
89                 domain, p->mem_ctx, r->in.domain, r->in.name, r->in.flags,
90                 r->out.sid, r->out.type);
91 }
92
93 NTSTATUS _wbint_Sid2Uid(struct pipes_struct *p, struct wbint_Sid2Uid *r)
94 {
95         uid_t uid;
96         NTSTATUS status;
97
98         status = idmap_sid_to_uid(r->in.dom_name ? r->in.dom_name : "",
99                                   r->in.sid, &uid);
100         if (!NT_STATUS_IS_OK(status)) {
101                 return status;
102         }
103         *r->out.uid = uid;
104         return NT_STATUS_OK;
105 }
106
107 NTSTATUS _wbint_Sid2Gid(struct pipes_struct *p, struct wbint_Sid2Gid *r)
108 {
109         gid_t gid;
110         NTSTATUS status;
111
112         status = idmap_sid_to_gid(r->in.dom_name ? r->in.dom_name : "",
113                                   r->in.sid, &gid);
114         if (!NT_STATUS_IS_OK(status)) {
115                 return status;
116         }
117         *r->out.gid = gid;
118         return NT_STATUS_OK;
119 }
120
121 NTSTATUS _wbint_Sids2UnixIDs(struct pipes_struct *p,
122                              struct wbint_Sids2UnixIDs *r)
123 {
124         uint32_t i, j;
125         struct id_map *ids = NULL;
126         struct id_map **id_ptrs = NULL;
127         struct dom_sid *sids = NULL;
128         uint32_t *id_idx = NULL;
129         NTSTATUS status = NT_STATUS_NO_MEMORY;
130
131         for (i=0; i<r->in.domains->count; i++) {
132                 struct lsa_DomainInfo *d = &r->in.domains->domains[i];
133                 struct idmap_domain *dom;
134                 uint32_t num_ids;
135
136                 dom = idmap_find_domain(d->name.string);
137                 if (dom == NULL) {
138                         DEBUG(10, ("idmap domain %s not found\n",
139                                    d->name.string));
140                         continue;
141                 }
142
143                 num_ids = 0;
144
145                 for (j=0; j<r->in.ids->num_ids; j++) {
146                         if (r->in.ids->ids[j].domain_index == i) {
147                                 num_ids += 1;
148                         }
149                 }
150
151                 ids = talloc_realloc(talloc_tos(), ids,
152                                            struct id_map, num_ids);
153                 if (ids == NULL) {
154                         goto nomem;
155                 }
156                 id_ptrs = talloc_realloc(talloc_tos(), id_ptrs,
157                                                struct id_map *, num_ids+1);
158                 if (id_ptrs == NULL) {
159                         goto nomem;
160                 }
161                 id_idx = talloc_realloc(talloc_tos(), id_idx,
162                                               uint32_t, num_ids);
163                 if (id_idx == NULL) {
164                         goto nomem;
165                 }
166                 sids = talloc_realloc(talloc_tos(), sids,
167                                             struct dom_sid, num_ids);
168                 if (sids == NULL) {
169                         goto nomem;
170                 }
171
172                 num_ids = 0;
173
174                 for (j=0; j<r->in.ids->num_ids; j++) {
175                         struct wbint_TransID *id = &r->in.ids->ids[j];
176
177                         if (id->domain_index != i) {
178                                 continue;
179                         }
180                         id_idx[num_ids] = j;
181                         id_ptrs[num_ids] = &ids[num_ids];
182
183                         ids[num_ids].sid = &sids[num_ids];
184                         sid_compose(ids[num_ids].sid, d->sid, id->rid);
185                         ids[num_ids].xid.type = id->type;
186                         ids[num_ids].status = ID_UNKNOWN;
187                         num_ids += 1;
188                 }
189                 id_ptrs[num_ids] = NULL;
190
191                 status = dom->methods->sids_to_unixids(dom, id_ptrs);
192                 DEBUG(10, ("sids_to_unixids returned %s\n",
193                            nt_errstr(status)));
194
195                 for (j=0; j<num_ids; j++) {
196                         struct wbint_TransID *id = &r->in.ids->ids[id_idx[j]];
197
198                         if (ids[j].status != ID_MAPPED) {
199                                 continue;
200                         }
201                         id->unix_id = ids[j].xid.id;
202                 }
203         }
204         status = NT_STATUS_OK;
205 nomem:
206         TALLOC_FREE(ids);
207         TALLOC_FREE(id_ptrs);
208         TALLOC_FREE(id_idx);
209         TALLOC_FREE(sids);
210         return status;
211 }
212
213 NTSTATUS _wbint_Uid2Sid(struct pipes_struct *p, struct wbint_Uid2Sid *r)
214 {
215         return idmap_uid_to_sid(r->in.dom_name ? r->in.dom_name : "",
216                                 r->out.sid, r->in.uid);
217 }
218
219 NTSTATUS _wbint_Gid2Sid(struct pipes_struct *p, struct wbint_Gid2Sid *r)
220 {
221         return idmap_gid_to_sid(r->in.dom_name ? r->in.dom_name : "",
222                                 r->out.sid, r->in.gid);
223 }
224
225 NTSTATUS _wbint_AllocateUid(struct pipes_struct *p, struct wbint_AllocateUid *r)
226 {
227         struct unixid xid;
228         NTSTATUS status;
229
230         status = idmap_allocate_uid(&xid);
231         if (!NT_STATUS_IS_OK(status)) {
232                 return status;
233         }
234         *r->out.uid = xid.id;
235         return NT_STATUS_OK;
236 }
237
238 NTSTATUS _wbint_AllocateGid(struct pipes_struct *p, struct wbint_AllocateGid *r)
239 {
240         struct unixid xid;
241         NTSTATUS status;
242
243         status = idmap_allocate_gid(&xid);
244         if (!NT_STATUS_IS_OK(status)) {
245                 return status;
246         }
247         *r->out.gid = xid.id;
248         return NT_STATUS_OK;
249 }
250
251 NTSTATUS _wbint_QueryUser(struct pipes_struct *p, struct wbint_QueryUser *r)
252 {
253         struct winbindd_domain *domain = wb_child_domain();
254
255         if (domain == NULL) {
256                 return NT_STATUS_REQUEST_NOT_ACCEPTED;
257         }
258
259         return domain->methods->query_user(domain, p->mem_ctx, r->in.sid,
260                                            r->out.info);
261 }
262
263 NTSTATUS _wbint_LookupUserAliases(struct pipes_struct *p,
264                                   struct wbint_LookupUserAliases *r)
265 {
266         struct winbindd_domain *domain = wb_child_domain();
267
268         if (domain == NULL) {
269                 return NT_STATUS_REQUEST_NOT_ACCEPTED;
270         }
271
272         return domain->methods->lookup_useraliases(
273                 domain, p->mem_ctx, r->in.sids->num_sids, r->in.sids->sids,
274                 &r->out.rids->num_rids, &r->out.rids->rids);
275 }
276
277 NTSTATUS _wbint_LookupUserGroups(struct pipes_struct *p,
278                                  struct wbint_LookupUserGroups *r)
279 {
280         struct winbindd_domain *domain = wb_child_domain();
281
282         if (domain == NULL) {
283                 return NT_STATUS_REQUEST_NOT_ACCEPTED;
284         }
285
286         return domain->methods->lookup_usergroups(
287                 domain, p->mem_ctx, r->in.sid,
288                 &r->out.sids->num_sids, &r->out.sids->sids);
289 }
290
291 NTSTATUS _wbint_QuerySequenceNumber(struct pipes_struct *p,
292                                     struct wbint_QuerySequenceNumber *r)
293 {
294         struct winbindd_domain *domain = wb_child_domain();
295
296         if (domain == NULL) {
297                 return NT_STATUS_REQUEST_NOT_ACCEPTED;
298         }
299
300         return domain->methods->sequence_number(domain, r->out.sequence);
301 }
302
303 NTSTATUS _wbint_LookupGroupMembers(struct pipes_struct *p,
304                                    struct wbint_LookupGroupMembers *r)
305 {
306         struct winbindd_domain *domain = wb_child_domain();
307         uint32_t i, num_names;
308         struct dom_sid *sid_mem;
309         char **names;
310         uint32_t *name_types;
311         NTSTATUS status;
312
313         if (domain == NULL) {
314                 return NT_STATUS_REQUEST_NOT_ACCEPTED;
315         }
316
317         status = domain->methods->lookup_groupmem(
318                 domain, p->mem_ctx, r->in.sid, r->in.type,
319                 &num_names, &sid_mem, &names, &name_types);
320         if (!NT_STATUS_IS_OK(status)) {
321                 return status;
322         }
323
324         r->out.members->num_principals = num_names;
325         r->out.members->principals = talloc_array(
326                 r->out.members, struct wbint_Principal, num_names);
327         if (r->out.members->principals == NULL) {
328                 return NT_STATUS_NO_MEMORY;
329         }
330
331         for (i=0; i<num_names; i++) {
332                 struct wbint_Principal *m = &r->out.members->principals[i];
333                 sid_copy(&m->sid, &sid_mem[i]);
334                 m->name = talloc_move(r->out.members->principals, &names[i]);
335                 m->type = (enum lsa_SidType)name_types[i];
336         }
337
338         return NT_STATUS_OK;
339 }
340
341 NTSTATUS _wbint_QueryUserList(struct pipes_struct *p,
342                               struct wbint_QueryUserList *r)
343 {
344         struct winbindd_domain *domain = wb_child_domain();
345
346         if (domain == NULL) {
347                 return NT_STATUS_REQUEST_NOT_ACCEPTED;
348         }
349
350         return domain->methods->query_user_list(
351                 domain, p->mem_ctx, &r->out.users->num_userinfos,
352                 &r->out.users->userinfos);
353 }
354
355 NTSTATUS _wbint_QueryGroupList(struct pipes_struct *p,
356                                struct wbint_QueryGroupList *r)
357 {
358         struct winbindd_domain *domain = wb_child_domain();
359         uint32_t i, num_groups;
360         struct wb_acct_info *groups;
361         struct wbint_Principal *result;
362         NTSTATUS status;
363
364         if (domain == NULL) {
365                 return NT_STATUS_REQUEST_NOT_ACCEPTED;
366         }
367
368         status = domain->methods->enum_dom_groups(domain, talloc_tos(),
369                                                   &num_groups, &groups);
370         if (!NT_STATUS_IS_OK(status)) {
371                 return status;
372         }
373
374         result = talloc_array(r->out.groups, struct wbint_Principal,
375                               num_groups);
376         if (result == NULL) {
377                 return NT_STATUS_NO_MEMORY;
378         }
379
380         for (i=0; i<num_groups; i++) {
381                 sid_compose(&result[i].sid, &domain->sid, groups[i].rid);
382                 result[i].type = SID_NAME_DOM_GRP;
383                 result[i].name = talloc_strdup(result, groups[i].acct_name);
384                 if (result[i].name == NULL) {
385                         TALLOC_FREE(result);
386                         TALLOC_FREE(groups);
387                         return NT_STATUS_NO_MEMORY;
388                 }
389         }
390
391         r->out.groups->num_principals = num_groups;
392         r->out.groups->principals = result;
393
394         TALLOC_FREE(groups);
395         return NT_STATUS_OK;
396 }
397
398 NTSTATUS _wbint_DsGetDcName(struct pipes_struct *p, struct wbint_DsGetDcName *r)
399 {
400         struct winbindd_domain *domain = wb_child_domain();
401         struct rpc_pipe_client *netlogon_pipe;
402         struct netr_DsRGetDCNameInfo *dc_info;
403         NTSTATUS status;
404         WERROR werr;
405         unsigned int orig_timeout;
406         struct dcerpc_binding_handle *b;
407
408         if (domain == NULL) {
409                 return dsgetdcname(p->mem_ctx, winbind_messaging_context(),
410                                    r->in.domain_name, r->in.domain_guid,
411                                    r->in.site_name ? r->in.site_name : "",
412                                    r->in.flags,
413                                    r->out.dc_info);
414         }
415
416         status = cm_connect_netlogon(domain, &netlogon_pipe);
417
418         if (!NT_STATUS_IS_OK(status)) {
419                 DEBUG(10, ("Can't contact the NETLOGON pipe\n"));
420                 return status;
421         }
422
423         b = netlogon_pipe->binding_handle;
424
425         /* This call can take a long time - allow the server to time out.
426            35 seconds should do it. */
427
428         orig_timeout = rpccli_set_timeout(netlogon_pipe, 35000);
429
430         if (domain->active_directory) {
431                 status = dcerpc_netr_DsRGetDCName(b,
432                         p->mem_ctx, domain->dcname,
433                         r->in.domain_name, NULL, r->in.domain_guid,
434                         r->in.flags, r->out.dc_info, &werr);
435                 if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(werr)) {
436                         goto done;
437                 }
438         }
439
440         /*
441          * Fallback to less capable methods
442          */
443
444         dc_info = talloc_zero(r->out.dc_info, struct netr_DsRGetDCNameInfo);
445         if (dc_info == NULL) {
446                 status = NT_STATUS_NO_MEMORY;
447                 goto done;
448         }
449
450         if (r->in.flags & DS_PDC_REQUIRED) {
451                 status = dcerpc_netr_GetDcName(b,
452                         p->mem_ctx, domain->dcname,
453                         r->in.domain_name, &dc_info->dc_unc, &werr);
454         } else {
455                 status = dcerpc_netr_GetAnyDCName(b,
456                         p->mem_ctx, domain->dcname,
457                         r->in.domain_name, &dc_info->dc_unc, &werr);
458         }
459
460         if (!NT_STATUS_IS_OK(status)) {
461                 DEBUG(10, ("dcerpc_netr_Get[Any]DCName failed: %s\n",
462                            nt_errstr(status)));
463                 goto done;
464         }
465         if (!W_ERROR_IS_OK(werr)) {
466                 DEBUG(10, ("dcerpc_netr_Get[Any]DCName failed: %s\n",
467                            win_errstr(werr)));
468                 status = werror_to_ntstatus(werr);
469                 goto done;
470         }
471
472         *r->out.dc_info = dc_info;
473         status = NT_STATUS_OK;
474
475 done:
476         /* And restore our original timeout. */
477         rpccli_set_timeout(netlogon_pipe, orig_timeout);
478
479         return status;
480 }
481
482 NTSTATUS _wbint_LookupRids(struct pipes_struct *p, struct wbint_LookupRids *r)
483 {
484         struct winbindd_domain *domain = wb_child_domain();
485         char *domain_name;
486         char **names;
487         enum lsa_SidType *types;
488         struct wbint_Principal *result;
489         NTSTATUS status;
490         int i;
491
492         if (domain == NULL) {
493                 return NT_STATUS_REQUEST_NOT_ACCEPTED;
494         }
495
496         status = domain->methods->rids_to_names(
497                 domain, talloc_tos(), &domain->sid, r->in.rids->rids,
498                 r->in.rids->num_rids, &domain_name, &names, &types);
499         if (!NT_STATUS_IS_OK(status)) {
500                 return status;
501         }
502
503         *r->out.domain_name = talloc_move(r->out.domain_name, &domain_name);
504
505         result = talloc_array(p->mem_ctx, struct wbint_Principal,
506                               r->in.rids->num_rids);
507         if (result == NULL) {
508                 return NT_STATUS_NO_MEMORY;
509         }
510
511         for (i=0; i<r->in.rids->num_rids; i++) {
512                 sid_compose(&result[i].sid, &domain->sid, r->in.rids->rids[i]);
513                 result[i].type = types[i];
514                 result[i].name = talloc_move(result, &names[i]);
515         }
516         TALLOC_FREE(types);
517         TALLOC_FREE(names);
518
519         r->out.names->num_principals = r->in.rids->num_rids;
520         r->out.names->principals = result;
521         return NT_STATUS_OK;
522 }
523
524 NTSTATUS _wbint_CheckMachineAccount(struct pipes_struct *p,
525                                     struct wbint_CheckMachineAccount *r)
526 {
527         struct winbindd_domain *domain;
528         int num_retries = 0;
529         NTSTATUS status;
530
531         domain = wb_child_domain();
532         if (domain == NULL) {
533                 return NT_STATUS_REQUEST_NOT_ACCEPTED;
534         }
535
536 again:
537         invalidate_cm_connection(&domain->conn);
538
539         {
540                 struct rpc_pipe_client *netlogon_pipe;
541                 status = cm_connect_netlogon(domain, &netlogon_pipe);
542         }
543
544         /* There is a race condition between fetching the trust account
545            password and the periodic machine password change.  So it's
546            possible that the trust account password has been changed on us.
547            We are returned NT_STATUS_ACCESS_DENIED if this happens. */
548
549 #define MAX_RETRIES 3
550
551         if ((num_retries < MAX_RETRIES)
552             && NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
553                 num_retries++;
554                 goto again;
555         }
556
557         if (!NT_STATUS_IS_OK(status)) {
558                 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
559                 goto done;
560         }
561
562         /* Pass back result code - zero for success, other values for
563            specific failures. */
564
565         DEBUG(3,("domain %s secret is %s\n", domain->name,
566                 NT_STATUS_IS_OK(status) ? "good" : "bad"));
567
568  done:
569         DEBUG(NT_STATUS_IS_OK(status) ? 5 : 2,
570               ("Checking the trust account password for domain %s returned %s\n",
571                domain->name, nt_errstr(status)));
572
573         return status;
574 }
575
576 NTSTATUS _wbint_ChangeMachineAccount(struct pipes_struct *p,
577                                      struct wbint_ChangeMachineAccount *r)
578 {
579         struct winbindd_domain *domain;
580         int num_retries = 0;
581         NTSTATUS status;
582         struct rpc_pipe_client *netlogon_pipe;
583         TALLOC_CTX *tmp_ctx;
584
585 again:
586         domain = wb_child_domain();
587         if (domain == NULL) {
588                 return NT_STATUS_REQUEST_NOT_ACCEPTED;
589         }
590
591         invalidate_cm_connection(&domain->conn);
592
593         {
594                 status = cm_connect_netlogon(domain, &netlogon_pipe);
595         }
596
597         /* There is a race condition between fetching the trust account
598            password and the periodic machine password change.  So it's
599            possible that the trust account password has been changed on us.
600            We are returned NT_STATUS_ACCESS_DENIED if this happens. */
601
602 #define MAX_RETRIES 3
603
604         if ((num_retries < MAX_RETRIES)
605              && NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
606                 num_retries++;
607                 goto again;
608         }
609
610         if (!NT_STATUS_IS_OK(status)) {
611                 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
612                 goto done;
613         }
614
615         tmp_ctx = talloc_new(p->mem_ctx);
616
617         status = trust_pw_find_change_and_store_it(netlogon_pipe,
618                                                    tmp_ctx,
619                                                    domain->name);
620         talloc_destroy(tmp_ctx);
621
622         /* Pass back result code - zero for success, other values for
623            specific failures. */
624
625         DEBUG(3,("domain %s secret %s\n", domain->name,
626                 NT_STATUS_IS_OK(status) ? "changed" : "unchanged"));
627
628  done:
629         DEBUG(NT_STATUS_IS_OK(status) ? 5 : 2,
630               ("Changing the trust account password for domain %s returned %s\n",
631                domain->name, nt_errstr(status)));
632
633         return status;
634 }
635
636 NTSTATUS _wbint_PingDc(struct pipes_struct *p, struct wbint_PingDc *r)
637 {
638         NTSTATUS status;
639         struct winbindd_domain *domain;
640         struct rpc_pipe_client *netlogon_pipe;
641         union netr_CONTROL_QUERY_INFORMATION info;
642         WERROR werr;
643         fstring logon_server;
644         struct dcerpc_binding_handle *b;
645
646         domain = wb_child_domain();
647         if (domain == NULL) {
648                 return NT_STATUS_REQUEST_NOT_ACCEPTED;
649         }
650
651         status = cm_connect_netlogon(domain, &netlogon_pipe);
652         if (!NT_STATUS_IS_OK(status)) {
653                 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
654                 return status;
655         }
656
657         b = netlogon_pipe->binding_handle;
658
659         fstr_sprintf(logon_server, "\\\\%s", domain->dcname);
660
661         /*
662          * This provokes a WERR_NOT_SUPPORTED error message. This is
663          * documented in the wspp docs. I could not get a successful
664          * call to work, but the main point here is testing that the
665          * netlogon pipe works.
666          */
667         status = dcerpc_netr_LogonControl(b, p->mem_ctx,
668                                           logon_server, NETLOGON_CONTROL_QUERY,
669                                           2, &info, &werr);
670
671         if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
672                 DEBUG(2, ("dcerpc_netr_LogonControl timed out\n"));
673                 invalidate_cm_connection(&domain->conn);
674                 return status;
675         }
676
677         if (!NT_STATUS_IS_OK(status)) {
678                 DEBUG(2, ("dcerpc_netr_LogonControl failed: %s\n",
679                         nt_errstr(status)));
680                 return status;
681         }
682
683         if (!W_ERROR_EQUAL(werr, WERR_NOT_SUPPORTED)) {
684                 DEBUG(2, ("dcerpc_netr_LogonControl returned %s, expected "
685                           "WERR_NOT_SUPPORTED\n",
686                           win_errstr(werr)));
687                 return werror_to_ntstatus(werr);
688         }
689
690         DEBUG(5, ("winbindd_dual_ping_dc succeeded\n"));
691         return NT_STATUS_OK;
692 }