987926e398a115b3f621bf32eca5747573a47eb5
[samba.git] / source / 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         struct winbindd_domain *req_domain;
240
241         state->request.domain_name
242                 [sizeof(state->request.domain_name)-1] = '\0';
243
244         DEBUG(3, ("[%5lu]: Get DC name for %s\n", (unsigned long)state->pid,
245                   state->request.domain_name));
246
247         result = cm_connect_netlogon(domain, &netlogon_pipe);
248
249         if (!NT_STATUS_IS_OK(result)) {
250                 DEBUG(1, ("Can't contact the NETLOGON pipe\n"));
251                 return WINBINDD_ERROR;
252         }
253
254         /* This call can take a long time - allow the server to time out.
255            35 seconds should do it. */
256
257         orig_timeout = cli_set_timeout(netlogon_pipe->cli, 35000);
258
259         req_domain = find_domain_from_name_noinit(state->request.domain_name);
260         if (req_domain == domain) {
261                 werr = rpccli_netlogon_getdcname(netlogon_pipe, state->mem_ctx,
262                                                  domain->dcname,
263                                                  state->request.domain_name,
264                                                  dcname_slash);
265         } else {
266                 werr = rpccli_netlogon_getanydcname(netlogon_pipe, state->mem_ctx,
267                                                     domain->dcname,
268                                                     state->request.domain_name,
269                                                     dcname_slash);
270         }
271         /* And restore our original timeout. */
272         cli_set_timeout(netlogon_pipe->cli, orig_timeout);
273
274         if (!W_ERROR_IS_OK(werr)) {
275                 DEBUG(5, ("Error requesting DCname for domain %s: %s\n",
276                         state->request.domain_name, dos_errstr(werr)));
277                 return WINBINDD_ERROR;
278         }
279
280         p = dcname_slash;
281         if (*p == '\\') {
282                 p+=1;
283         }
284         if (*p == '\\') {
285                 p+=1;
286         }
287
288         fstrcpy(state->response.data.dc_name, p);
289         return WINBINDD_OK;
290 }
291
292 struct sequence_state {
293         TALLOC_CTX *mem_ctx;
294         struct winbindd_cli_state *cli_state;
295         struct winbindd_domain *domain;
296         struct winbindd_request *request;
297         struct winbindd_response *response;
298         char *extra_data;
299 };
300
301 static void sequence_recv(void *private_data, BOOL success);
302
303 void winbindd_show_sequence(struct winbindd_cli_state *state)
304 {
305         struct sequence_state *seq;
306
307         /* Ensure null termination */
308         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
309
310         if (strlen(state->request.domain_name) > 0) {
311                 struct winbindd_domain *domain;
312                 domain = find_domain_from_name_noinit(
313                         state->request.domain_name);
314                 if (domain == NULL) {
315                         request_error(state);
316                         return;
317                 }
318                 sendto_domain(state, domain);
319                 return;
320         }
321
322         /* Ask all domains in sequence, collect the results in sequence_recv */
323
324         seq = TALLOC_P(state->mem_ctx, struct sequence_state);
325         if (seq == NULL) {
326                 DEBUG(0, ("talloc failed\n"));
327                 request_error(state);
328                 return;
329         }
330
331         seq->mem_ctx = state->mem_ctx;
332         seq->cli_state = state;
333         seq->domain = domain_list();
334         if (seq->domain == NULL) {
335                 DEBUG(0, ("domain list empty\n"));
336                 request_error(state);
337                 return;
338         }
339         seq->request = TALLOC_ZERO_P(state->mem_ctx,
340                                      struct winbindd_request);
341         seq->response = TALLOC_ZERO_P(state->mem_ctx,
342                                       struct winbindd_response);
343         seq->extra_data = talloc_strdup(state->mem_ctx, "");
344
345         if ((seq->request == NULL) || (seq->response == NULL) ||
346             (seq->extra_data == NULL)) {
347                 DEBUG(0, ("talloc failed\n"));
348                 request_error(state);
349                 return;
350         }
351
352         seq->request->length = sizeof(*seq->request);
353         seq->request->cmd = WINBINDD_SHOW_SEQUENCE;
354         fstrcpy(seq->request->domain_name, seq->domain->name);
355
356         async_domain_request(state->mem_ctx, seq->domain,
357                              seq->request, seq->response,
358                              sequence_recv, seq);
359 }
360
361 static void sequence_recv(void *private_data, BOOL success)
362 {
363         struct sequence_state *state =
364                 (struct sequence_state *)private_data;
365         uint32 seq = DOM_SEQUENCE_NONE;
366
367         if ((success) && (state->response->result == WINBINDD_OK))
368                 seq = state->response->data.sequence_number;
369
370         if (seq == DOM_SEQUENCE_NONE) {
371                 state->extra_data = talloc_asprintf(state->mem_ctx,
372                                                     "%s%s : DISCONNECTED\n",
373                                                     state->extra_data,
374                                                     state->domain->name);
375         } else {
376                 state->extra_data = talloc_asprintf(state->mem_ctx,
377                                                     "%s%s : %d\n",
378                                                     state->extra_data,
379                                                     state->domain->name, seq);
380         }
381
382         state->domain->sequence_number = seq;
383
384         state->domain = state->domain->next;
385
386         if (state->domain == NULL) {
387                 struct winbindd_cli_state *cli_state = state->cli_state;
388                 cli_state->response.length =
389                         sizeof(cli_state->response) +
390                         strlen(state->extra_data) + 1;
391                 cli_state->response.extra_data.data =
392                         SMB_STRDUP(state->extra_data);
393                 request_ok(cli_state);
394                 return;
395         }
396
397         /* Ask the next domain */
398         fstrcpy(state->request->domain_name, state->domain->name);
399         async_domain_request(state->mem_ctx, state->domain,
400                              state->request, state->response,
401                              sequence_recv, state);
402 }
403
404 /* This is the child-only version of --sequence. It only allows for a single
405  * domain (ie "our" one) to be displayed. */
406
407 enum winbindd_result winbindd_dual_show_sequence(struct winbindd_domain *domain,
408                                                  struct winbindd_cli_state *state)
409 {
410         DEBUG(3, ("[%5lu]: show sequence\n", (unsigned long)state->pid));
411
412         /* Ensure null termination */
413         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
414
415         domain->methods->sequence_number(domain, &domain->sequence_number);
416
417         state->response.data.sequence_number =
418                 domain->sequence_number;
419
420         return WINBINDD_OK;
421 }
422
423 struct domain_info_state {
424         struct winbindd_domain *domain;
425         struct winbindd_cli_state *cli_state;
426 };
427
428 static void domain_info_init_recv(void *private_data, BOOL success);
429
430 void winbindd_domain_info(struct winbindd_cli_state *state)
431 {
432         struct winbindd_domain *domain;
433
434         DEBUG(3, ("[%5lu]: domain_info [%s]\n", (unsigned long)state->pid,
435                   state->request.domain_name));
436
437         domain = find_domain_from_name_noinit(state->request.domain_name);
438
439         if (domain == NULL) {
440                 DEBUG(3, ("Did not find domain [%s]\n",
441                           state->request.domain_name));
442                 request_error(state);
443                 return;
444         }
445
446         if (!domain->initialized) {
447                 struct domain_info_state *istate;
448
449                 istate = TALLOC_P(state->mem_ctx, struct domain_info_state);
450                 if (istate == NULL) {
451                         DEBUG(0, ("talloc failed\n"));
452                         request_error(state);
453                         return;
454                 }
455
456                 istate->cli_state = state;
457                 istate->domain = domain;
458
459                 init_child_connection(domain, domain_info_init_recv, istate);
460                                       
461                 return;
462         }
463
464         fstrcpy(state->response.data.domain_info.name,
465                 domain->name);
466         fstrcpy(state->response.data.domain_info.alt_name,
467                 domain->alt_name);
468         fstrcpy(state->response.data.domain_info.sid,
469                 sid_string_static(&domain->sid));
470         
471         state->response.data.domain_info.native_mode =
472                 domain->native_mode;
473         state->response.data.domain_info.active_directory =
474                 domain->active_directory;
475         state->response.data.domain_info.primary =
476                 domain->primary;
477
478         request_ok(state);
479 }
480
481 static void domain_info_init_recv(void *private_data, BOOL success)
482 {
483         struct domain_info_state *istate =
484                 (struct domain_info_state *)private_data;
485         struct winbindd_cli_state *state = istate->cli_state;
486         struct winbindd_domain *domain = istate->domain;
487
488         DEBUG(10, ("Got back from child init: %d\n", success));
489
490         if ((!success) || (!domain->initialized)) {
491                 DEBUG(5, ("Could not init child for domain %s\n",
492                           domain->name));
493                 request_error(state);
494                 return;
495         }
496
497         fstrcpy(state->response.data.domain_info.name,
498                 domain->name);
499         fstrcpy(state->response.data.domain_info.alt_name,
500                 domain->alt_name);
501         fstrcpy(state->response.data.domain_info.sid,
502                 sid_string_static(&domain->sid));
503         
504         state->response.data.domain_info.native_mode =
505                 domain->native_mode;
506         state->response.data.domain_info.active_directory =
507                 domain->active_directory;
508         state->response.data.domain_info.primary =
509                 domain->primary;
510
511         request_ok(state);
512 }
513
514 void winbindd_ping(struct winbindd_cli_state *state)
515 {
516         DEBUG(3, ("[%5lu]: ping\n", (unsigned long)state->pid));
517         request_ok(state);
518 }
519
520 /* List various tidbits of information */
521
522 void winbindd_info(struct winbindd_cli_state *state)
523 {
524
525         DEBUG(3, ("[%5lu]: request misc info\n", (unsigned long)state->pid));
526
527         state->response.data.info.winbind_separator = *lp_winbind_separator();
528         fstrcpy(state->response.data.info.samba_version, SAMBA_VERSION_STRING);
529         request_ok(state);
530 }
531
532 /* Tell the client the current interface version */
533
534 void winbindd_interface_version(struct winbindd_cli_state *state)
535 {
536         DEBUG(3, ("[%5lu]: request interface version\n",
537                   (unsigned long)state->pid));
538         
539         state->response.data.interface_version = WINBIND_INTERFACE_VERSION;
540         request_ok(state);
541 }
542
543 /* What domain are we a member of? */
544
545 void winbindd_domain_name(struct winbindd_cli_state *state)
546 {
547         DEBUG(3, ("[%5lu]: request domain name\n", (unsigned long)state->pid));
548         
549         fstrcpy(state->response.data.domain_name, lp_workgroup());
550         request_ok(state);
551 }
552
553 /* What's my name again? */
554
555 void winbindd_netbios_name(struct winbindd_cli_state *state)
556 {
557         DEBUG(3, ("[%5lu]: request netbios name\n",
558                   (unsigned long)state->pid));
559         
560         fstrcpy(state->response.data.netbios_name, global_myname());
561         request_ok(state);
562 }
563
564 /* Where can I find the privilaged pipe? */
565
566 void winbindd_priv_pipe_dir(struct winbindd_cli_state *state)
567 {
568
569         DEBUG(3, ("[%5lu]: request location of privileged pipe\n",
570                   (unsigned long)state->pid));
571         
572         state->response.extra_data.data = SMB_STRDUP(get_winbind_priv_pipe_dir());
573         if (!state->response.extra_data.data) {
574                 DEBUG(0, ("malloc failed\n"));
575                 request_error(state);
576                 return;
577         }
578
579         /* must add one to length to copy the 0 for string termination */
580         state->response.length +=
581                 strlen((char *)state->response.extra_data.data) + 1;
582
583         request_ok(state);
584 }
585