s3:winbind: Convert WINBINDD_LIST_GROUPS to the new API
[kai/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         set_auth_errors(state->response, result);
90
91         DEBUG(NT_STATUS_IS_OK(result) ? 5 : 2, ("Checking the trust account password returned %s\n", 
92                                                 state->response->data.auth.nt_status_string));
93
94         return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
95 }
96
97 /* Constants and helper functions for determining domain trust types */
98
99 enum trust_type {
100         EXTERNAL = 0,
101         FOREST,
102         IN_FOREST,
103         NONE,
104 };
105
106 const char *trust_type_strings[] = {"External", 
107                                     "Forest", 
108                                     "In Forest",
109                                     "None"};
110
111 static enum trust_type get_trust_type(struct winbindd_tdc_domain *domain)
112 {
113         if (domain->trust_attribs == NETR_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN)   
114                 return EXTERNAL;
115         else if (domain->trust_attribs == NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE)
116                 return FOREST;
117         else if (((domain->trust_flags & NETR_TRUST_FLAG_IN_FOREST) == NETR_TRUST_FLAG_IN_FOREST) &&
118             ((domain->trust_flags & NETR_TRUST_FLAG_PRIMARY) == 0x0))
119                 return IN_FOREST;
120         return NONE;    
121 }
122
123 static const char *get_trust_type_string(struct winbindd_tdc_domain *domain)
124 {
125         return trust_type_strings[get_trust_type(domain)];
126 }
127
128 static bool trust_is_inbound(struct winbindd_tdc_domain *domain)
129 {
130         return (domain->trust_flags == 0x0) ||
131             ((domain->trust_flags & NETR_TRUST_FLAG_IN_FOREST) ==
132             NETR_TRUST_FLAG_IN_FOREST) ||                       
133             ((domain->trust_flags & NETR_TRUST_FLAG_INBOUND) ==
134             NETR_TRUST_FLAG_INBOUND);           
135 }
136
137 static bool trust_is_outbound(struct winbindd_tdc_domain *domain)
138 {
139         return (domain->trust_flags == 0x0) ||
140             ((domain->trust_flags & NETR_TRUST_FLAG_IN_FOREST) ==
141             NETR_TRUST_FLAG_IN_FOREST) ||                       
142             ((domain->trust_flags & NETR_TRUST_FLAG_OUTBOUND) ==
143             NETR_TRUST_FLAG_OUTBOUND);          
144 }
145
146 static bool trust_is_transitive(struct winbindd_tdc_domain *domain)
147 {
148         if ((domain->trust_attribs == NETR_TRUST_ATTRIBUTE_NON_TRANSITIVE) ||         
149             (domain->trust_attribs == NETR_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN) ||
150             (domain->trust_attribs == NETR_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL))
151                 return False;
152         return True;
153 }
154
155 void winbindd_list_trusted_domains(struct winbindd_cli_state *state)
156 {
157         struct winbindd_tdc_domain *dom_list = NULL;
158         struct winbindd_tdc_domain *d = NULL;
159         size_t num_domains = 0;
160         int extra_data_len = 0;
161         char *extra_data = NULL;
162         int i = 0;
163         
164         DEBUG(3, ("[%5lu]: list trusted domains\n",
165                   (unsigned long)state->pid));
166
167         if( !wcache_tdc_fetch_list( &dom_list, &num_domains )) {
168                 request_error(state);   
169                 goto done;
170         }
171
172         for ( i = 0; i < num_domains; i++ ) {
173                 struct winbindd_domain *domain;
174                 bool is_online = true;          
175
176                 d = &dom_list[i];
177                 domain = find_domain_from_name_noinit(d->domain_name);
178                 if (domain) {
179                         is_online = domain->online;
180                 }
181
182                 if ( !extra_data ) {
183                         extra_data = talloc_asprintf(state->mem_ctx, 
184                                                      "%s\\%s\\%s\\%s\\%s\\%s\\%s\\%s",
185                                                      d->domain_name,
186                                                      d->dns_name ? d->dns_name : d->domain_name,
187                                                      sid_string_talloc(state->mem_ctx, &d->sid),
188                                                      get_trust_type_string(d),
189                                                      trust_is_transitive(d) ? "Yes" : "No",
190                                                      trust_is_inbound(d) ? "Yes" : "No",
191                                                      trust_is_outbound(d) ? "Yes" : "No",
192                                                      is_online ? "Online" : "Offline" );
193                 } else {
194                         extra_data = talloc_asprintf(state->mem_ctx, 
195                                                      "%s\n%s\\%s\\%s\\%s\\%s\\%s\\%s\\%s",
196                                                      extra_data,
197                                                      d->domain_name,
198                                                      d->dns_name ? d->dns_name : d->domain_name,
199                                                      sid_string_talloc(state->mem_ctx, &d->sid),
200                                                      get_trust_type_string(d),
201                                                      trust_is_transitive(d) ? "Yes" : "No",
202                                                      trust_is_inbound(d) ? "Yes" : "No",
203                                                      trust_is_outbound(d) ? "Yes" : "No",
204                                                      is_online ? "Online" : "Offline" );
205                 }
206         }
207         
208         extra_data_len = 0;
209         if (extra_data != NULL) {
210                 extra_data_len = strlen(extra_data);
211         }
212
213         if (extra_data_len > 0) {
214                 state->response->extra_data.data = extra_data;
215                 state->response->length += extra_data_len+1;
216         }
217
218         request_ok(state);      
219 done:
220         TALLOC_FREE( dom_list );
221 }
222
223 enum winbindd_result winbindd_dual_list_trusted_domains(struct winbindd_domain *domain,
224                                                         struct winbindd_cli_state *state)
225 {
226         uint32 i, num_domains;
227         char **names, **alt_names;
228         DOM_SID *sids;
229         int extra_data_len = 0;
230         char *extra_data;
231         NTSTATUS result;
232         bool have_own_domain = False;
233
234         DEBUG(3, ("[%5lu]: list trusted domains\n",
235                   (unsigned long)state->pid));
236
237         result = domain->methods->trusted_domains(domain, state->mem_ctx,
238                                                   &num_domains, &names,
239                                                   &alt_names, &sids);
240
241         if (!NT_STATUS_IS_OK(result)) {
242                 DEBUG(3, ("winbindd_dual_list_trusted_domains: trusted_domains returned %s\n",
243                         nt_errstr(result) ));
244                 return WINBINDD_ERROR;
245         }
246
247         extra_data = talloc_strdup(state->mem_ctx, "");
248
249         if (num_domains > 0)
250                 extra_data = talloc_asprintf(
251                         state->mem_ctx, "%s\\%s\\%s",
252                         names[0], alt_names[0] ? alt_names[0] : names[0],
253                         sid_string_talloc(state->mem_ctx, &sids[0]));
254
255         for (i=1; i<num_domains; i++)
256                 extra_data = talloc_asprintf(
257                         state->mem_ctx, "%s\n%s\\%s\\%s",
258                         extra_data, names[i],
259                         alt_names[i] ? alt_names[i] : names[i],
260                         sid_string_talloc(state->mem_ctx, &sids[i]));
261
262         /* add our primary domain */
263         
264         for (i=0; i<num_domains; i++) {
265                 if (strequal(names[i], domain->name)) {
266                         have_own_domain = True;
267                         break;
268                 }
269         }
270
271         if (state->request->data.list_all_domains && !have_own_domain) {
272                 extra_data = talloc_asprintf(
273                         state->mem_ctx, "%s\n%s\\%s\\%s",
274                         extra_data, domain->name,
275                         domain->alt_name ? domain->alt_name : domain->name,
276                         sid_string_talloc(state->mem_ctx, &domain->sid));
277         }
278
279         /* This is a bit excessive, but the extra data sooner or later will be
280            talloc'ed */
281
282         extra_data_len = 0;
283         if (extra_data != NULL) {
284                 extra_data_len = strlen(extra_data);
285         }
286
287         if (extra_data_len > 0) {
288                 state->response->extra_data.data = extra_data;
289                 state->response->length += extra_data_len+1;
290         }
291
292         return WINBINDD_OK;
293 }
294
295 enum winbindd_result winbindd_dual_getdcname(struct winbindd_domain *domain,
296                                              struct winbindd_cli_state *state)
297 {
298         const char *dcname_slash = NULL;
299         const char *p;
300         struct rpc_pipe_client *netlogon_pipe;
301         NTSTATUS result;
302         WERROR werr;
303         unsigned int orig_timeout;
304         struct winbindd_domain *req_domain;
305
306         state->request->domain_name
307                 [sizeof(state->request->domain_name)-1] = '\0';
308
309         DEBUG(3, ("[%5lu]: Get DC name for %s\n", (unsigned long)state->pid,
310                   state->request->domain_name));
311
312         result = cm_connect_netlogon(domain, &netlogon_pipe);
313
314         if (!NT_STATUS_IS_OK(result)) {
315                 DEBUG(1, ("Can't contact the NETLOGON pipe\n"));
316                 return WINBINDD_ERROR;
317         }
318
319         /* This call can take a long time - allow the server to time out.
320            35 seconds should do it. */
321
322         orig_timeout = rpccli_set_timeout(netlogon_pipe, 35000);
323
324         req_domain = find_domain_from_name_noinit(state->request->domain_name);
325         if (req_domain == domain) {
326                 result = rpccli_netr_GetDcName(netlogon_pipe,
327                                                state->mem_ctx,
328                                                domain->dcname,
329                                                state->request->domain_name,
330                                                &dcname_slash,
331                                                &werr);
332         } else {
333                 result = rpccli_netr_GetAnyDCName(netlogon_pipe,
334                                                   state->mem_ctx,
335                                                   domain->dcname,
336                                                   state->request->domain_name,
337                                                   &dcname_slash,
338                                                   &werr);
339         }
340         /* And restore our original timeout. */
341         rpccli_set_timeout(netlogon_pipe, orig_timeout);
342
343         if (!NT_STATUS_IS_OK(result)) {
344                 DEBUG(5,("Error requesting DCname for domain %s: %s\n",
345                         state->request->domain_name, nt_errstr(result)));
346                 return WINBINDD_ERROR;
347         }
348
349         if (!W_ERROR_IS_OK(werr)) {
350                 DEBUG(5, ("Error requesting DCname for domain %s: %s\n",
351                         state->request->domain_name, win_errstr(werr)));
352                 return WINBINDD_ERROR;
353         }
354
355         p = dcname_slash;
356         if (*p == '\\') {
357                 p+=1;
358         }
359         if (*p == '\\') {
360                 p+=1;
361         }
362
363         fstrcpy(state->response->data.dc_name, p);
364         return WINBINDD_OK;
365 }
366
367 /* This is the child-only version of --sequence. It only allows for a single
368  * domain (ie "our" one) to be displayed. */
369
370 enum winbindd_result winbindd_dual_show_sequence(struct winbindd_domain *domain,
371                                                  struct winbindd_cli_state *state)
372 {
373         DEBUG(3, ("[%5lu]: show sequence\n", (unsigned long)state->pid));
374
375         /* Ensure null termination */
376         state->request->domain_name[sizeof(state->request->domain_name)-1]='\0';
377
378         domain->methods->sequence_number(domain, &domain->sequence_number);
379
380         state->response->data.sequence_number =
381                 domain->sequence_number;
382
383         return WINBINDD_OK;
384 }
385
386 struct domain_info_state {
387         struct winbindd_domain *domain;
388         struct winbindd_cli_state *cli;
389         struct winbindd_request ping_request;
390 };
391
392 static void domain_info_done(struct tevent_req *req);
393
394 void winbindd_domain_info(struct winbindd_cli_state *cli)
395 {
396         struct domain_info_state *state;
397         struct winbindd_domain *domain;
398         struct tevent_req *req;
399
400         DEBUG(3, ("[%5lu]: domain_info [%s]\n", (unsigned long)cli->pid,
401                   cli->request->domain_name));
402
403         domain = find_domain_from_name_noinit(cli->request->domain_name);
404
405         if (domain == NULL) {
406                 DEBUG(3, ("Did not find domain [%s]\n",
407                           cli->request->domain_name));
408                 request_error(cli);
409                 return;
410         }
411
412         if (domain->initialized) {
413                 fstrcpy(cli->response->data.domain_info.name,
414                         domain->name);
415                 fstrcpy(cli->response->data.domain_info.alt_name,
416                         domain->alt_name);
417                 sid_to_fstring(cli->response->data.domain_info.sid,
418                                &domain->sid);
419                 cli->response->data.domain_info.native_mode =
420                         domain->native_mode;
421                 cli->response->data.domain_info.active_directory =
422                         domain->active_directory;
423                 cli->response->data.domain_info.primary =
424                         domain->primary;
425                 request_ok(cli);
426                 return;
427         }
428
429         state = talloc_zero(cli->mem_ctx, struct domain_info_state);
430         if (state == NULL) {
431                 DEBUG(0, ("talloc failed\n"));
432                 request_error(cli);
433                 return;
434         }
435
436         state->cli = cli;
437         state->domain = domain;
438         state->ping_request.cmd = WINBINDD_PING;
439
440         /*
441          * Send a ping down. This implicitly initializes the domain.
442          */
443
444         req = wb_domain_request_send(state, winbind_event_context(),
445                                      domain, &state->ping_request);
446         if (req == NULL) {
447                 DEBUG(3, ("wb_domain_request_send failed\n"));
448                 request_error(cli);
449         }
450         tevent_req_set_callback(req, domain_info_done, state);
451 }
452
453 static void domain_info_done(struct tevent_req *req)
454 {
455         struct domain_info_state *state = tevent_req_callback_data(
456                 req, struct domain_info_state);
457         struct winbindd_response *response;
458         int ret, err;
459
460         ret = wb_domain_request_recv(req, req, &response, &err);
461         TALLOC_FREE(req);
462         if (ret == -1) {
463                 DEBUG(10, ("wb_domain_request failed: %s\n", strerror(errno)));
464                 request_error(state->cli);
465                 return;
466         }
467         if (!state->domain->initialized) {
468                 DEBUG(5, ("wb_domain_request did not initialize domain %s\n",
469                           state->domain->name));
470                 request_error(state->cli);
471                 return;
472         }
473
474         fstrcpy(state->cli->response->data.domain_info.name,
475                 state->domain->name);
476         fstrcpy(state->cli->response->data.domain_info.alt_name,
477                 state->domain->alt_name);
478         sid_to_fstring(state->cli->response->data.domain_info.sid,
479                        &state->domain->sid);
480
481         state->cli->response->data.domain_info.native_mode =
482                 state->domain->native_mode;
483         state->cli->response->data.domain_info.active_directory =
484                 state->domain->active_directory;
485         state->cli->response->data.domain_info.primary =
486                 state->domain->primary;
487
488         request_ok(state->cli);
489 }
490
491 /* List various tidbits of information */
492
493 void winbindd_info(struct winbindd_cli_state *state)
494 {
495
496         DEBUG(3, ("[%5lu]: request misc info\n", (unsigned long)state->pid));
497
498         state->response->data.info.winbind_separator = *lp_winbind_separator();
499         fstrcpy(state->response->data.info.samba_version, samba_version_string());
500         request_ok(state);
501 }
502
503 /* Tell the client the current interface version */
504
505 void winbindd_interface_version(struct winbindd_cli_state *state)
506 {
507         DEBUG(3, ("[%5lu]: request interface version\n",
508                   (unsigned long)state->pid));
509         
510         state->response->data.interface_version = WINBIND_INTERFACE_VERSION;
511         request_ok(state);
512 }
513
514 /* What domain are we a member of? */
515
516 void winbindd_domain_name(struct winbindd_cli_state *state)
517 {
518         DEBUG(3, ("[%5lu]: request domain name\n", (unsigned long)state->pid));
519         
520         fstrcpy(state->response->data.domain_name, lp_workgroup());
521         request_ok(state);
522 }
523
524 /* What's my name again? */
525
526 void winbindd_netbios_name(struct winbindd_cli_state *state)
527 {
528         DEBUG(3, ("[%5lu]: request netbios name\n",
529                   (unsigned long)state->pid));
530         
531         fstrcpy(state->response->data.netbios_name, global_myname());
532         request_ok(state);
533 }
534
535 /* Where can I find the privilaged pipe? */
536
537 void winbindd_priv_pipe_dir(struct winbindd_cli_state *state)
538 {
539         char *priv_dir;
540         DEBUG(3, ("[%5lu]: request location of privileged pipe\n",
541                   (unsigned long)state->pid));
542         
543         priv_dir = get_winbind_priv_pipe_dir();
544         state->response->extra_data.data = talloc_move(state->mem_ctx,
545                                                       &priv_dir);
546
547         /* must add one to length to copy the 0 for string termination */
548         state->response->length +=
549                 strlen((char *)state->response->extra_data.data) + 1;
550
551         request_ok(state);
552 }
553