Prevent winbindd from segfaulting due to corrupted cache tdb.
[ira/wip.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(
112                                 state->mem_ctx, "%s\\%s\\%s",
113                                 d->name, d->alt_name ? d->alt_name : d->name,
114                                 sid_string_talloc(state->mem_ctx, &d->sid));
115                 } else {
116                         extra_data = talloc_asprintf(
117                                 state->mem_ctx, "%s\n%s\\%s\\%s",
118                                 extra_data, d->name,
119                                 d->alt_name ? d->alt_name : d->name,
120                                 sid_string_talloc(state->mem_ctx, &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(
167                         state->mem_ctx, "%s\\%s\\%s",
168                         names[0], alt_names[0] ? alt_names[0] : names[0],
169                         sid_string_talloc(state->mem_ctx, &sids[0]));
170
171         for (i=1; i<num_domains; i++)
172                 extra_data = talloc_asprintf(
173                         state->mem_ctx, "%s\n%s\\%s\\%s",
174                         extra_data, names[i],
175                         alt_names[i] ? alt_names[i] : names[i],
176                         sid_string_talloc(state->mem_ctx, &sids[i]));
177
178         /* add our primary domain */
179         
180         for (i=0; i<num_domains; i++) {
181                 if (strequal(names[i], domain->name)) {
182                         have_own_domain = True;
183                         break;
184                 }
185         }
186
187         if (state->request.data.list_all_domains && !have_own_domain) {
188                 extra_data = talloc_asprintf(
189                         state->mem_ctx, "%s\n%s\\%s\\%s",
190                         extra_data, domain->name,
191                         domain->alt_name ? domain->alt_name : domain->name,
192                         sid_string_talloc(state->mem_ctx, &domain->sid));
193         }
194
195         /* This is a bit excessive, but the extra data sooner or later will be
196            talloc'ed */
197
198         extra_data_len = 0;
199         if (extra_data != NULL) {
200                 extra_data_len = strlen(extra_data);
201         }
202
203         if (extra_data_len > 0) {
204                 state->response.extra_data.data = SMB_STRDUP(extra_data);
205                 state->response.length += extra_data_len+1;
206         }
207
208         return WINBINDD_OK;
209 }
210
211 void winbindd_getdcname(struct winbindd_cli_state *state)
212 {
213         struct winbindd_domain *domain;
214
215         state->request.domain_name
216                 [sizeof(state->request.domain_name)-1] = '\0';
217
218         DEBUG(3, ("[%5lu]: Get DC name for %s\n", (unsigned long)state->pid,
219                   state->request.domain_name));
220
221         domain = find_domain_from_name_noinit(state->request.domain_name);
222         if (domain && domain->internal) {
223                 fstrcpy(state->response.data.dc_name, global_myname());
224                 request_ok(state);      
225                 return;
226         }
227
228         sendto_domain(state, find_our_domain());
229 }
230
231 enum winbindd_result winbindd_dual_getdcname(struct winbindd_domain *domain,
232                                              struct winbindd_cli_state *state)
233 {
234         char *dcname_slash = NULL;
235         char *p;
236         struct rpc_pipe_client *netlogon_pipe;
237         NTSTATUS result;
238         WERROR werr;
239         unsigned int orig_timeout;
240         struct winbindd_domain *req_domain;
241
242         state->request.domain_name
243                 [sizeof(state->request.domain_name)-1] = '\0';
244
245         DEBUG(3, ("[%5lu]: Get DC name for %s\n", (unsigned long)state->pid,
246                   state->request.domain_name));
247
248         result = cm_connect_netlogon(domain, &netlogon_pipe);
249
250         if (!NT_STATUS_IS_OK(result)) {
251                 DEBUG(1, ("Can't contact the NETLOGON pipe\n"));
252                 return WINBINDD_ERROR;
253         }
254
255         /* This call can take a long time - allow the server to time out.
256            35 seconds should do it. */
257
258         orig_timeout = cli_set_timeout(netlogon_pipe->cli, 35000);
259
260         req_domain = find_domain_from_name_noinit(state->request.domain_name);
261         if (req_domain == domain) {
262                 werr = rpccli_netlogon_getdcname(netlogon_pipe, state->mem_ctx,
263                                                  domain->dcname,
264                                                  state->request.domain_name,
265                                                  &dcname_slash);
266         } else {
267                 werr = rpccli_netlogon_getanydcname(netlogon_pipe, state->mem_ctx,
268                                                     domain->dcname,
269                                                     state->request.domain_name,
270                                                     &dcname_slash);
271         }
272         /* And restore our original timeout. */
273         cli_set_timeout(netlogon_pipe->cli, orig_timeout);
274
275         if (!W_ERROR_IS_OK(werr)) {
276                 DEBUG(5, ("Error requesting DCname for domain %s: %s\n",
277                         state->request.domain_name, dos_errstr(werr)));
278                 return WINBINDD_ERROR;
279         }
280
281         p = dcname_slash;
282         if (*p == '\\') {
283                 p+=1;
284         }
285         if (*p == '\\') {
286                 p+=1;
287         }
288
289         fstrcpy(state->response.data.dc_name, p);
290         return WINBINDD_OK;
291 }
292
293 struct sequence_state {
294         TALLOC_CTX *mem_ctx;
295         struct winbindd_cli_state *cli_state;
296         struct winbindd_domain *domain;
297         struct winbindd_request *request;
298         struct winbindd_response *response;
299         char *extra_data;
300 };
301
302 static void sequence_recv(void *private_data, bool success);
303
304 void winbindd_show_sequence(struct winbindd_cli_state *state)
305 {
306         struct sequence_state *seq;
307
308         /* Ensure null termination */
309         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
310
311         if (strlen(state->request.domain_name) > 0) {
312                 struct winbindd_domain *domain;
313                 domain = find_domain_from_name_noinit(
314                         state->request.domain_name);
315                 if (domain == NULL) {
316                         request_error(state);
317                         return;
318                 }
319                 sendto_domain(state, domain);
320                 return;
321         }
322
323         /* Ask all domains in sequence, collect the results in sequence_recv */
324
325         seq = TALLOC_P(state->mem_ctx, struct sequence_state);
326         if (seq == NULL) {
327                 DEBUG(0, ("talloc failed\n"));
328                 request_error(state);
329                 return;
330         }
331
332         seq->mem_ctx = state->mem_ctx;
333         seq->cli_state = state;
334         seq->domain = domain_list();
335         if (seq->domain == NULL) {
336                 DEBUG(0, ("domain list empty\n"));
337                 request_error(state);
338                 return;
339         }
340         seq->request = TALLOC_ZERO_P(state->mem_ctx,
341                                      struct winbindd_request);
342         seq->response = TALLOC_ZERO_P(state->mem_ctx,
343                                       struct winbindd_response);
344         seq->extra_data = talloc_strdup(state->mem_ctx, "");
345
346         if ((seq->request == NULL) || (seq->response == NULL) ||
347             (seq->extra_data == NULL)) {
348                 DEBUG(0, ("talloc failed\n"));
349                 request_error(state);
350                 return;
351         }
352
353         seq->request->length = sizeof(*seq->request);
354         seq->request->cmd = WINBINDD_SHOW_SEQUENCE;
355         fstrcpy(seq->request->domain_name, seq->domain->name);
356
357         async_domain_request(state->mem_ctx, seq->domain,
358                              seq->request, seq->response,
359                              sequence_recv, seq);
360 }
361
362 static void sequence_recv(void *private_data, bool success)
363 {
364         struct sequence_state *state =
365                 (struct sequence_state *)private_data;
366         uint32 seq = DOM_SEQUENCE_NONE;
367
368         if ((success) && (state->response->result == WINBINDD_OK))
369                 seq = state->response->data.sequence_number;
370
371         if (seq == DOM_SEQUENCE_NONE) {
372                 state->extra_data = talloc_asprintf(state->mem_ctx,
373                                                     "%s%s : DISCONNECTED\n",
374                                                     state->extra_data,
375                                                     state->domain->name);
376         } else {
377                 state->extra_data = talloc_asprintf(state->mem_ctx,
378                                                     "%s%s : %d\n",
379                                                     state->extra_data,
380                                                     state->domain->name, seq);
381         }
382
383         state->domain->sequence_number = seq;
384
385         state->domain = state->domain->next;
386
387         if (state->domain == NULL) {
388                 struct winbindd_cli_state *cli_state = state->cli_state;
389                 cli_state->response.length =
390                         sizeof(cli_state->response) +
391                         strlen(state->extra_data) + 1;
392                 cli_state->response.extra_data.data =
393                         SMB_STRDUP(state->extra_data);
394                 request_ok(cli_state);
395                 return;
396         }
397
398         /* Ask the next domain */
399         fstrcpy(state->request->domain_name, state->domain->name);
400         async_domain_request(state->mem_ctx, state->domain,
401                              state->request, state->response,
402                              sequence_recv, state);
403 }
404
405 /* This is the child-only version of --sequence. It only allows for a single
406  * domain (ie "our" one) to be displayed. */
407
408 enum winbindd_result winbindd_dual_show_sequence(struct winbindd_domain *domain,
409                                                  struct winbindd_cli_state *state)
410 {
411         DEBUG(3, ("[%5lu]: show sequence\n", (unsigned long)state->pid));
412
413         /* Ensure null termination */
414         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
415
416         domain->methods->sequence_number(domain, &domain->sequence_number);
417
418         state->response.data.sequence_number =
419                 domain->sequence_number;
420
421         return WINBINDD_OK;
422 }
423
424 struct domain_info_state {
425         struct winbindd_domain *domain;
426         struct winbindd_cli_state *cli_state;
427 };
428
429 static void domain_info_init_recv(void *private_data, bool success);
430
431 void winbindd_domain_info(struct winbindd_cli_state *state)
432 {
433         struct winbindd_domain *domain;
434
435         DEBUG(3, ("[%5lu]: domain_info [%s]\n", (unsigned long)state->pid,
436                   state->request.domain_name));
437
438         domain = find_domain_from_name_noinit(state->request.domain_name);
439
440         if (domain == NULL) {
441                 DEBUG(3, ("Did not find domain [%s]\n",
442                           state->request.domain_name));
443                 request_error(state);
444                 return;
445         }
446
447         if (!domain->initialized) {
448                 struct domain_info_state *istate;
449
450                 istate = TALLOC_P(state->mem_ctx, struct domain_info_state);
451                 if (istate == NULL) {
452                         DEBUG(0, ("talloc failed\n"));
453                         request_error(state);
454                         return;
455                 }
456
457                 istate->cli_state = state;
458                 istate->domain = domain;
459
460                 init_child_connection(domain, domain_info_init_recv, istate);
461                                       
462                 return;
463         }
464
465         fstrcpy(state->response.data.domain_info.name,
466                 domain->name);
467         fstrcpy(state->response.data.domain_info.alt_name,
468                 domain->alt_name);
469         sid_to_fstring(state->response.data.domain_info.sid, &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         sid_to_fstring(state->response.data.domain_info.sid, &domain->sid);
502         
503         state->response.data.domain_info.native_mode =
504                 domain->native_mode;
505         state->response.data.domain_info.active_directory =
506                 domain->active_directory;
507         state->response.data.domain_info.primary =
508                 domain->primary;
509
510         request_ok(state);
511 }
512
513 void winbindd_ping(struct winbindd_cli_state *state)
514 {
515         DEBUG(3, ("[%5lu]: ping\n", (unsigned long)state->pid));
516         request_ok(state);
517 }
518
519 /* List various tidbits of information */
520
521 void winbindd_info(struct winbindd_cli_state *state)
522 {
523
524         DEBUG(3, ("[%5lu]: request misc info\n", (unsigned long)state->pid));
525
526         state->response.data.info.winbind_separator = *lp_winbind_separator();
527         fstrcpy(state->response.data.info.samba_version, SAMBA_VERSION_STRING);
528         request_ok(state);
529 }
530
531 /* Tell the client the current interface version */
532
533 void winbindd_interface_version(struct winbindd_cli_state *state)
534 {
535         DEBUG(3, ("[%5lu]: request interface version\n",
536                   (unsigned long)state->pid));
537         
538         state->response.data.interface_version = WINBIND_INTERFACE_VERSION;
539         request_ok(state);
540 }
541
542 /* What domain are we a member of? */
543
544 void winbindd_domain_name(struct winbindd_cli_state *state)
545 {
546         DEBUG(3, ("[%5lu]: request domain name\n", (unsigned long)state->pid));
547         
548         fstrcpy(state->response.data.domain_name, lp_workgroup());
549         request_ok(state);
550 }
551
552 /* What's my name again? */
553
554 void winbindd_netbios_name(struct winbindd_cli_state *state)
555 {
556         DEBUG(3, ("[%5lu]: request netbios name\n",
557                   (unsigned long)state->pid));
558         
559         fstrcpy(state->response.data.netbios_name, global_myname());
560         request_ok(state);
561 }
562
563 /* Where can I find the privilaged pipe? */
564
565 void winbindd_priv_pipe_dir(struct winbindd_cli_state *state)
566 {
567
568         DEBUG(3, ("[%5lu]: request location of privileged pipe\n",
569                   (unsigned long)state->pid));
570         
571         state->response.extra_data.data = SMB_STRDUP(get_winbind_priv_pipe_dir());
572         if (!state->response.extra_data.data) {
573                 DEBUG(0, ("malloc failed\n"));
574                 request_error(state);
575                 return;
576         }
577
578         /* must add one to length to copy the 0 for string termination */
579         state->response.length +=
580                 strlen((char *)state->response.extra_data.data) + 1;
581
582         request_ok(state);
583 }
584