r25270: for internal domains we should not ask a remote DC.
[samba.git] / source3 / winbindd / winbindd_misc.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon - miscellaneous other functions
5
6    Copyright (C) Tim Potter      2000
7    Copyright (C) Andrew Bartlett 2002
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.h"
25
26 #undef DBGC_CLASS
27 #define DBGC_CLASS DBGC_WINBIND
28
29 /* Check the machine account password is valid */
30
31 void winbindd_check_machine_acct(struct winbindd_cli_state *state)
32 {
33         DEBUG(3, ("[%5lu]: check machine account\n",
34                   (unsigned long)state->pid));
35
36         sendto_domain(state, find_our_domain());
37 }
38
39 enum winbindd_result winbindd_dual_check_machine_acct(struct winbindd_domain *domain,
40                                                       struct winbindd_cli_state *state)
41 {
42         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
43         int num_retries = 0;
44         struct winbindd_domain *contact_domain;
45
46         DEBUG(3, ("[%5lu]: check machine account\n", (unsigned long)state->pid));
47
48         /* Get trust account password */
49
50  again:
51
52         contact_domain = find_our_domain();
53         
54         /* This call does a cli_nt_setup_creds() which implicitly checks
55            the trust account password. */
56
57         invalidate_cm_connection(&contact_domain->conn);
58
59         {
60                 struct rpc_pipe_client *netlogon_pipe;
61                 result = cm_connect_netlogon(contact_domain, &netlogon_pipe);
62         }
63
64         if (!NT_STATUS_IS_OK(result)) {
65                 DEBUG(3, ("could not open handle to NETLOGON pipe\n"));
66                 goto done;
67         }
68
69         /* There is a race condition between fetching the trust account
70            password and the periodic machine password change.  So it's 
71            possible that the trust account password has been changed on us.  
72            We are returned NT_STATUS_ACCESS_DENIED if this happens. */
73
74 #define MAX_RETRIES 8
75
76         if ((num_retries < MAX_RETRIES) && 
77             NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED)) {
78                 num_retries++;
79                 goto again;
80         }
81
82         /* Pass back result code - zero for success, other values for
83            specific failures. */
84
85         DEBUG(3, ("secret is %s\n", NT_STATUS_IS_OK(result) ?  
86                   "good" : "bad"));
87
88  done:
89         state->response.data.auth.nt_status = NT_STATUS_V(result);
90         fstrcpy(state->response.data.auth.nt_status_string, nt_errstr(result));
91         fstrcpy(state->response.data.auth.error_string, nt_errstr(result));
92         state->response.data.auth.pam_error = nt_status_to_pam(result);
93
94         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Checking the trust account password returned %s\n", 
95                                                 state->response.data.auth.nt_status_string));
96
97         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
98 }
99
100 void winbindd_list_trusted_domains(struct winbindd_cli_state *state)
101 {
102         struct winbindd_domain *d = NULL;
103         int extra_data_len = 0;
104         char *extra_data = NULL;
105         
106         DEBUG(3, ("[%5lu]: list trusted domains\n",
107                   (unsigned long)state->pid));
108
109         for ( d=domain_list(); d; d=d->next ) {
110                 if ( !extra_data ) {
111                         extra_data = talloc_asprintf(state->mem_ctx, "%s\\%s\\%s",
112                                                      d->name,
113                                                      d->alt_name ? d->alt_name : d->name,
114                                                      sid_string_static(&d->sid));
115                 } else {
116                         extra_data = talloc_asprintf(state->mem_ctx, "%s\n%s\\%s\\%s",
117                                                      extra_data,
118                                                      d->name,
119                                                      d->alt_name ? d->alt_name : d->name,
120                                                      sid_string_static(&d->sid));
121                 }
122         }
123         
124         extra_data_len = 0;
125         if (extra_data != NULL) {
126                 extra_data_len = strlen(extra_data);
127         }
128
129         if (extra_data_len > 0) {
130                 state->response.extra_data.data = SMB_STRDUP(extra_data);
131                 state->response.length += extra_data_len+1;
132         }
133
134         TALLOC_FREE( extra_data );      
135
136         request_ok(state);      
137 }
138
139 enum winbindd_result winbindd_dual_list_trusted_domains(struct winbindd_domain *domain,
140                                                         struct winbindd_cli_state *state)
141 {
142         uint32 i, num_domains;
143         char **names, **alt_names;
144         DOM_SID *sids;
145         int extra_data_len = 0;
146         char *extra_data;
147         NTSTATUS result;
148         BOOL have_own_domain = False;
149
150         DEBUG(3, ("[%5lu]: list trusted domains\n",
151                   (unsigned long)state->pid));
152
153         result = domain->methods->trusted_domains(domain, state->mem_ctx,
154                                                   &num_domains, &names,
155                                                   &alt_names, &sids);
156
157         if (!NT_STATUS_IS_OK(result)) {
158                 DEBUG(3, ("winbindd_dual_list_trusted_domains: trusted_domains returned %s\n",
159                         nt_errstr(result) ));
160                 return WINBINDD_ERROR;
161         }
162
163         extra_data = talloc_strdup(state->mem_ctx, "");
164
165         if (num_domains > 0)
166                 extra_data = talloc_asprintf(state->mem_ctx, "%s\\%s\\%s",
167                                              names[0],
168                                              alt_names[0] ? alt_names[0] : names[0],
169                                              sid_string_static(&sids[0]));
170
171         for (i=1; i<num_domains; i++)
172                 extra_data = talloc_asprintf(state->mem_ctx, "%s\n%s\\%s\\%s",
173                                              extra_data,
174                                              names[i],
175                                              alt_names[i] ? alt_names[i] : names[i],
176                                              sid_string_static(&sids[i]));
177         /* add our primary domain */
178         
179         for (i=0; i<num_domains; i++) {
180                 if (strequal(names[i], domain->name)) {
181                         have_own_domain = True;
182                         break;
183                 }
184         }
185
186         if (state->request.data.list_all_domains && !have_own_domain) {
187                 extra_data = talloc_asprintf(state->mem_ctx, "%s\n%s\\%s\\%s",
188                                              extra_data,
189                                              domain->name,
190                                              domain->alt_name ? domain->alt_name : domain->name,
191                                              sid_string_static(&domain->sid));
192         }
193
194         /* This is a bit excessive, but the extra data sooner or later will be
195            talloc'ed */
196
197         extra_data_len = 0;
198         if (extra_data != NULL) {
199                 extra_data_len = strlen(extra_data);
200         }
201
202         if (extra_data_len > 0) {
203                 state->response.extra_data.data = SMB_STRDUP(extra_data);
204                 state->response.length += extra_data_len+1;
205         }
206
207         return WINBINDD_OK;
208 }
209
210 void winbindd_getdcname(struct winbindd_cli_state *state)
211 {
212         struct winbindd_domain *domain;
213
214         state->request.domain_name
215                 [sizeof(state->request.domain_name)-1] = '\0';
216
217         DEBUG(3, ("[%5lu]: Get DC name for %s\n", (unsigned long)state->pid,
218                   state->request.domain_name));
219
220         domain = find_domain_from_name_noinit(state->request.domain_name);
221         if (domain && domain->internal) {
222                 fstrcpy(state->response.data.dc_name, global_myname());
223                 request_ok(state);      
224                 return;
225         }
226
227         sendto_domain(state, find_our_domain());
228 }
229
230 enum winbindd_result winbindd_dual_getdcname(struct winbindd_domain *domain,
231                                              struct winbindd_cli_state *state)
232 {
233         fstring dcname_slash;
234         char *p;
235         struct rpc_pipe_client *netlogon_pipe;
236         NTSTATUS result;
237         WERROR werr;
238         unsigned int orig_timeout;
239
240         state->request.domain_name
241                 [sizeof(state->request.domain_name)-1] = '\0';
242
243         DEBUG(3, ("[%5lu]: Get DC name for %s\n", (unsigned long)state->pid,
244                   state->request.domain_name));
245
246         result = cm_connect_netlogon(domain, &netlogon_pipe);
247
248         if (!NT_STATUS_IS_OK(result)) {
249                 DEBUG(1, ("Can't contact the NETLOGON pipe\n"));
250                 return WINBINDD_ERROR;
251         }
252
253         /* This call can take a long time - allow the server to time out.
254            35 seconds should do it. */
255
256         orig_timeout = cli_set_timeout(netlogon_pipe->cli, 35000);
257
258         werr = rpccli_netlogon_getanydcname(netlogon_pipe, state->mem_ctx, domain->dcname,
259                                             state->request.domain_name,
260                                             dcname_slash);
261         /* And restore our original timeout. */
262         cli_set_timeout(netlogon_pipe->cli, orig_timeout);
263
264         if (!W_ERROR_IS_OK(werr)) {
265                 DEBUG(5, ("Error requesting DCname for domain %s: %s\n",
266                         state->request.domain_name, dos_errstr(werr)));
267                 return WINBINDD_ERROR;
268         }
269
270         p = dcname_slash;
271         if (*p == '\\') {
272                 p+=1;
273         }
274         if (*p == '\\') {
275                 p+=1;
276         }
277
278         fstrcpy(state->response.data.dc_name, p);
279         return WINBINDD_OK;
280 }
281
282 static struct winbindd_child static_locator_child;
283
284 void init_locator_child(void)
285 {
286         setup_domain_child(NULL, &static_locator_child, "locator");
287 }
288
289 struct winbindd_child *locator_child(void)
290 {
291         return &static_locator_child;
292 }
293
294 void winbindd_dsgetdcname(struct winbindd_cli_state *state)
295 {
296         state->request.domain_name
297                 [sizeof(state->request.domain_name)-1] = '\0';
298
299         DEBUG(3, ("[%5lu]: DsGetDcName for %s\n", (unsigned long)state->pid,
300                   state->request.domain_name));
301
302         sendto_child(state, locator_child());
303 }
304
305 enum winbindd_result winbindd_dual_dsgetdcname(struct winbindd_domain *domain,
306                                                struct winbindd_cli_state *state)
307 {
308         NTSTATUS result;
309         struct DS_DOMAIN_CONTROLLER_INFO *info = NULL;
310         const char *dc = NULL;
311
312         state->request.domain_name
313                 [sizeof(state->request.domain_name)-1] = '\0';
314
315         DEBUG(3, ("[%5lu]: DsGetDcName for %s\n", (unsigned long)state->pid,
316                   state->request.domain_name));
317
318         result = DsGetDcName(state->mem_ctx, NULL, state->request.domain_name,
319                              NULL, NULL, state->request.flags, &info);
320
321         if (!NT_STATUS_IS_OK(result)) {
322                 return WINBINDD_ERROR;
323         }
324
325         if (info->domain_controller_address) {
326                 dc = info->domain_controller_address;
327                 if ((dc[0] == '\\') && (dc[1] == '\\')) {
328                         dc += 2;
329                 }
330         }
331
332         if ((!dc || !is_ipaddress(dc)) && info->domain_controller_name) {
333                 dc = info->domain_controller_name;
334         }
335
336         if (!dc || !*dc) {
337                 return WINBINDD_ERROR;
338         }
339
340         fstrcpy(state->response.data.dc_name, dc);
341
342         return WINBINDD_OK;
343 }
344
345
346 struct sequence_state {
347         TALLOC_CTX *mem_ctx;
348         struct winbindd_cli_state *cli_state;
349         struct winbindd_domain *domain;
350         struct winbindd_request *request;
351         struct winbindd_response *response;
352         char *extra_data;
353 };
354
355 static void sequence_recv(void *private_data, BOOL success);
356
357 void winbindd_show_sequence(struct winbindd_cli_state *state)
358 {
359         struct sequence_state *seq;
360
361         /* Ensure null termination */
362         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
363
364         if (strlen(state->request.domain_name) > 0) {
365                 struct winbindd_domain *domain;
366                 domain = find_domain_from_name_noinit(
367                         state->request.domain_name);
368                 if (domain == NULL) {
369                         request_error(state);
370                         return;
371                 }
372                 sendto_domain(state, domain);
373                 return;
374         }
375
376         /* Ask all domains in sequence, collect the results in sequence_recv */
377
378         seq = TALLOC_P(state->mem_ctx, struct sequence_state);
379         if (seq == NULL) {
380                 DEBUG(0, ("talloc failed\n"));
381                 request_error(state);
382                 return;
383         }
384
385         seq->mem_ctx = state->mem_ctx;
386         seq->cli_state = state;
387         seq->domain = domain_list();
388         if (seq->domain == NULL) {
389                 DEBUG(0, ("domain list empty\n"));
390                 request_error(state);
391                 return;
392         }
393         seq->request = TALLOC_ZERO_P(state->mem_ctx,
394                                      struct winbindd_request);
395         seq->response = TALLOC_ZERO_P(state->mem_ctx,
396                                       struct winbindd_response);
397         seq->extra_data = talloc_strdup(state->mem_ctx, "");
398
399         if ((seq->request == NULL) || (seq->response == NULL) ||
400             (seq->extra_data == NULL)) {
401                 DEBUG(0, ("talloc failed\n"));
402                 request_error(state);
403                 return;
404         }
405
406         seq->request->length = sizeof(*seq->request);
407         seq->request->cmd = WINBINDD_SHOW_SEQUENCE;
408         fstrcpy(seq->request->domain_name, seq->domain->name);
409
410         async_domain_request(state->mem_ctx, seq->domain,
411                              seq->request, seq->response,
412                              sequence_recv, seq);
413 }
414
415 static void sequence_recv(void *private_data, BOOL success)
416 {
417         struct sequence_state *state =
418                 (struct sequence_state *)private_data;
419         uint32 seq = DOM_SEQUENCE_NONE;
420
421         if ((success) && (state->response->result == WINBINDD_OK))
422                 seq = state->response->data.sequence_number;
423
424         if (seq == DOM_SEQUENCE_NONE) {
425                 state->extra_data = talloc_asprintf(state->mem_ctx,
426                                                     "%s%s : DISCONNECTED\n",
427                                                     state->extra_data,
428                                                     state->domain->name);
429         } else {
430                 state->extra_data = talloc_asprintf(state->mem_ctx,
431                                                     "%s%s : %d\n",
432                                                     state->extra_data,
433                                                     state->domain->name, seq);
434         }
435
436         state->domain->sequence_number = seq;
437
438         state->domain = state->domain->next;
439
440         if (state->domain == NULL) {
441                 struct winbindd_cli_state *cli_state = state->cli_state;
442                 cli_state->response.length =
443                         sizeof(cli_state->response) +
444                         strlen(state->extra_data) + 1;
445                 cli_state->response.extra_data.data =
446                         SMB_STRDUP(state->extra_data);
447                 request_ok(cli_state);
448                 return;
449         }
450
451         /* Ask the next domain */
452         fstrcpy(state->request->domain_name, state->domain->name);
453         async_domain_request(state->mem_ctx, state->domain,
454                              state->request, state->response,
455                              sequence_recv, state);
456 }
457
458 /* This is the child-only version of --sequence. It only allows for a single
459  * domain (ie "our" one) to be displayed. */
460
461 enum winbindd_result winbindd_dual_show_sequence(struct winbindd_domain *domain,
462                                                  struct winbindd_cli_state *state)
463 {
464         DEBUG(3, ("[%5lu]: show sequence\n", (unsigned long)state->pid));
465
466         /* Ensure null termination */
467         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
468
469         domain->methods->sequence_number(domain, &domain->sequence_number);
470
471         state->response.data.sequence_number =
472                 domain->sequence_number;
473
474         return WINBINDD_OK;
475 }
476
477 struct domain_info_state {
478         struct winbindd_domain *domain;
479         struct winbindd_cli_state *cli_state;
480 };
481
482 static void domain_info_init_recv(void *private_data, BOOL success);
483
484 void winbindd_domain_info(struct winbindd_cli_state *state)
485 {
486         struct winbindd_domain *domain;
487
488         DEBUG(3, ("[%5lu]: domain_info [%s]\n", (unsigned long)state->pid,
489                   state->request.domain_name));
490
491         domain = find_domain_from_name_noinit(state->request.domain_name);
492
493         if (domain == NULL) {
494                 DEBUG(3, ("Did not find domain [%s]\n",
495                           state->request.domain_name));
496                 request_error(state);
497                 return;
498         }
499
500         if (!domain->initialized) {
501                 struct domain_info_state *istate;
502
503                 istate = TALLOC_P(state->mem_ctx, struct domain_info_state);
504                 if (istate == NULL) {
505                         DEBUG(0, ("talloc failed\n"));
506                         request_error(state);
507                         return;
508                 }
509
510                 istate->cli_state = state;
511                 istate->domain = domain;
512
513                 init_child_connection(domain, domain_info_init_recv, istate);
514                                       
515                 return;
516         }
517
518         fstrcpy(state->response.data.domain_info.name,
519                 domain->name);
520         fstrcpy(state->response.data.domain_info.alt_name,
521                 domain->alt_name);
522         fstrcpy(state->response.data.domain_info.sid,
523                 sid_string_static(&domain->sid));
524         
525         state->response.data.domain_info.native_mode =
526                 domain->native_mode;
527         state->response.data.domain_info.active_directory =
528                 domain->active_directory;
529         state->response.data.domain_info.primary =
530                 domain->primary;
531
532         request_ok(state);
533 }
534
535 static void domain_info_init_recv(void *private_data, BOOL success)
536 {
537         struct domain_info_state *istate =
538                 (struct domain_info_state *)private_data;
539         struct winbindd_cli_state *state = istate->cli_state;
540         struct winbindd_domain *domain = istate->domain;
541
542         DEBUG(10, ("Got back from child init: %d\n", success));
543
544         if ((!success) || (!domain->initialized)) {
545                 DEBUG(5, ("Could not init child for domain %s\n",
546                           domain->name));
547                 request_error(state);
548                 return;
549         }
550
551         fstrcpy(state->response.data.domain_info.name,
552                 domain->name);
553         fstrcpy(state->response.data.domain_info.alt_name,
554                 domain->alt_name);
555         fstrcpy(state->response.data.domain_info.sid,
556                 sid_string_static(&domain->sid));
557         
558         state->response.data.domain_info.native_mode =
559                 domain->native_mode;
560         state->response.data.domain_info.active_directory =
561                 domain->active_directory;
562         state->response.data.domain_info.primary =
563                 domain->primary;
564
565         request_ok(state);
566 }
567
568 void winbindd_ping(struct winbindd_cli_state *state)
569 {
570         DEBUG(3, ("[%5lu]: ping\n", (unsigned long)state->pid));
571         request_ok(state);
572 }
573
574 /* List various tidbits of information */
575
576 void winbindd_info(struct winbindd_cli_state *state)
577 {
578
579         DEBUG(3, ("[%5lu]: request misc info\n", (unsigned long)state->pid));
580
581         state->response.data.info.winbind_separator = *lp_winbind_separator();
582         fstrcpy(state->response.data.info.samba_version, SAMBA_VERSION_STRING);
583         request_ok(state);
584 }
585
586 /* Tell the client the current interface version */
587
588 void winbindd_interface_version(struct winbindd_cli_state *state)
589 {
590         DEBUG(3, ("[%5lu]: request interface version\n",
591                   (unsigned long)state->pid));
592         
593         state->response.data.interface_version = WINBIND_INTERFACE_VERSION;
594         request_ok(state);
595 }
596
597 /* What domain are we a member of? */
598
599 void winbindd_domain_name(struct winbindd_cli_state *state)
600 {
601         DEBUG(3, ("[%5lu]: request domain name\n", (unsigned long)state->pid));
602         
603         fstrcpy(state->response.data.domain_name, lp_workgroup());
604         request_ok(state);
605 }
606
607 /* What's my name again? */
608
609 void winbindd_netbios_name(struct winbindd_cli_state *state)
610 {
611         DEBUG(3, ("[%5lu]: request netbios name\n",
612                   (unsigned long)state->pid));
613         
614         fstrcpy(state->response.data.netbios_name, global_myname());
615         request_ok(state);
616 }
617
618 /* Where can I find the privilaged pipe? */
619
620 void winbindd_priv_pipe_dir(struct winbindd_cli_state *state)
621 {
622
623         DEBUG(3, ("[%5lu]: request location of privileged pipe\n",
624                   (unsigned long)state->pid));
625         
626         state->response.extra_data.data = SMB_STRDUP(get_winbind_priv_pipe_dir());
627         if (!state->response.extra_data.data) {
628                 DEBUG(0, ("malloc failed\n"));
629                 request_error(state);
630                 return;
631         }
632
633         /* must add one to length to copy the 0 for string termination */
634         state->response.length +=
635                 strlen((char *)state->response.extra_data.data) + 1;
636
637         request_ok(state);
638 }
639