s3:winbind: Convert the GETDCNAME routines to the new API
[gd/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 /* Helpers for listing user and group names */
98
99 const char *ent_type_strings[] = {"users", 
100                                   "groups"};
101
102 static const char *get_ent_type_string(enum ent_type type)
103 {
104         return ent_type_strings[type];
105 }
106
107 struct listent_state {
108         TALLOC_CTX *mem_ctx;
109         struct winbindd_cli_state *cli_state;
110         enum ent_type type;
111         int domain_count;
112         char *extra_data;
113         uint32_t extra_data_len;
114 };
115
116 static void listent_recv(void *private_data, bool success, fstring dom_name,
117                          char *extra_data);
118
119 /* List domain users/groups without mapping to unix ids */
120 void winbindd_list_ent(struct winbindd_cli_state *state, enum ent_type type)
121 {
122         struct winbindd_domain *domain;
123         const char *which_domain;
124         struct listent_state *ent_state;
125
126         DEBUG(3, ("[%5lu]: list %s\n", (unsigned long)state->pid, 
127               get_ent_type_string(type)));
128
129         /* Ensure null termination */
130         state->request->domain_name[sizeof(state->request->domain_name)-1]='\0';
131         which_domain = state->request->domain_name;
132         
133         /* Initialize listent_state */
134         ent_state = TALLOC_P(state->mem_ctx, struct listent_state);
135         if (ent_state == NULL) {
136                 DEBUG(0, ("talloc failed\n"));
137                 request_error(state);
138                 return;
139         }
140
141         ent_state->mem_ctx = state->mem_ctx;
142         ent_state->cli_state = state;
143         ent_state->type = type;
144         ent_state->domain_count = 0;
145         ent_state->extra_data = NULL;
146         ent_state->extra_data_len = 0;
147
148         /* Must count the full list of expected domains before we request data
149          * from any of them.  Otherwise it's possible for a connection to the
150          * first domain to fail, call listent_recv(), and return to the
151          * client without checking any other domains. */
152         for (domain = domain_list(); domain; domain = domain->next) {
153                 /* if we have a domain name restricting the request and this
154                    one in the list doesn't match, then just bypass the remainder
155                    of the loop */
156                 if ( *which_domain && !strequal(which_domain, domain->name) )
157                         continue;
158
159                 ent_state->domain_count++;
160         }
161
162         /* Make sure we're enumerating at least one domain */
163         if (!ent_state->domain_count) {
164                 request_ok(state);
165                 return;
166         }
167
168         /* Enumerate list of trusted domains and request user/group list from
169          * each */
170         for (domain = domain_list(); domain; domain = domain->next) {
171                 if ( *which_domain && !strequal(which_domain, domain->name) )
172                         continue;
173
174                 winbindd_listent_async(state->mem_ctx, domain, 
175                                           listent_recv, ent_state, type);
176         }
177 }
178
179 static void listent_recv(void *private_data, bool success, fstring dom_name,
180                          char *extra_data)
181 {
182         /* extra_data comes to us as a '\0' terminated string of comma
183            separated users or groups */
184         struct listent_state *state = talloc_get_type_abort(
185                 private_data, struct listent_state);
186
187         /* Append users/groups from one domain onto the whole list */
188         if (extra_data) {
189                 DEBUG(5, ("listent_recv: %s returned %s.\n", 
190                       dom_name, get_ent_type_string(state->type)));
191                 if (!state->extra_data)
192                         state->extra_data = talloc_asprintf(state->mem_ctx,
193                                                             "%s", extra_data);
194                 else
195                         state->extra_data = talloc_asprintf_append(
196                                                             state->extra_data,
197                                                             ",%s", extra_data);
198                 /* Add one for the '\0' and each additional ',' */
199                 state->extra_data_len += strlen(extra_data) + 1;
200         }
201         else {
202                 DEBUG(5, ("listent_recv: %s returned no %s.\n", 
203                       dom_name, get_ent_type_string(state->type)));
204         }
205
206         if (--state->domain_count)
207                 /* Still waiting for some child domains to return */
208                 return;
209
210         /* Return list of all users/groups to the client */
211         if (state->extra_data) {
212                 state->cli_state->response->extra_data.data = state->extra_data;
213                 state->cli_state->response->length += state->extra_data_len;
214         }
215
216         request_ok(state->cli_state);
217 }       
218
219 /* Constants and helper functions for determining domain trust types */
220
221 enum trust_type {
222         EXTERNAL = 0,
223         FOREST,
224         IN_FOREST,
225         NONE,
226 };
227
228 const char *trust_type_strings[] = {"External", 
229                                     "Forest", 
230                                     "In Forest",
231                                     "None"};
232
233 static enum trust_type get_trust_type(struct winbindd_tdc_domain *domain)
234 {
235         if (domain->trust_attribs == NETR_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN)   
236                 return EXTERNAL;
237         else if (domain->trust_attribs == NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE)
238                 return FOREST;
239         else if (((domain->trust_flags & NETR_TRUST_FLAG_IN_FOREST) == NETR_TRUST_FLAG_IN_FOREST) &&
240             ((domain->trust_flags & NETR_TRUST_FLAG_PRIMARY) == 0x0))
241                 return IN_FOREST;
242         return NONE;    
243 }
244
245 static const char *get_trust_type_string(struct winbindd_tdc_domain *domain)
246 {
247         return trust_type_strings[get_trust_type(domain)];
248 }
249
250 static bool trust_is_inbound(struct winbindd_tdc_domain *domain)
251 {
252         return (domain->trust_flags == 0x0) ||
253             ((domain->trust_flags & NETR_TRUST_FLAG_IN_FOREST) ==
254             NETR_TRUST_FLAG_IN_FOREST) ||                       
255             ((domain->trust_flags & NETR_TRUST_FLAG_INBOUND) ==
256             NETR_TRUST_FLAG_INBOUND);           
257 }
258
259 static bool trust_is_outbound(struct winbindd_tdc_domain *domain)
260 {
261         return (domain->trust_flags == 0x0) ||
262             ((domain->trust_flags & NETR_TRUST_FLAG_IN_FOREST) ==
263             NETR_TRUST_FLAG_IN_FOREST) ||                       
264             ((domain->trust_flags & NETR_TRUST_FLAG_OUTBOUND) ==
265             NETR_TRUST_FLAG_OUTBOUND);          
266 }
267
268 static bool trust_is_transitive(struct winbindd_tdc_domain *domain)
269 {
270         if ((domain->trust_attribs == NETR_TRUST_ATTRIBUTE_NON_TRANSITIVE) ||         
271             (domain->trust_attribs == NETR_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN) ||
272             (domain->trust_attribs == NETR_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL))
273                 return False;
274         return True;
275 }
276
277 void winbindd_list_trusted_domains(struct winbindd_cli_state *state)
278 {
279         struct winbindd_tdc_domain *dom_list = NULL;
280         struct winbindd_tdc_domain *d = NULL;
281         size_t num_domains = 0;
282         int extra_data_len = 0;
283         char *extra_data = NULL;
284         int i = 0;
285         
286         DEBUG(3, ("[%5lu]: list trusted domains\n",
287                   (unsigned long)state->pid));
288
289         if( !wcache_tdc_fetch_list( &dom_list, &num_domains )) {
290                 request_error(state);   
291                 goto done;
292         }
293
294         for ( i = 0; i < num_domains; i++ ) {
295                 struct winbindd_domain *domain;
296                 bool is_online = true;          
297
298                 d = &dom_list[i];
299                 domain = find_domain_from_name_noinit(d->domain_name);
300                 if (domain) {
301                         is_online = domain->online;
302                 }
303
304                 if ( !extra_data ) {
305                         extra_data = talloc_asprintf(state->mem_ctx, 
306                                                      "%s\\%s\\%s\\%s\\%s\\%s\\%s\\%s",
307                                                      d->domain_name,
308                                                      d->dns_name ? d->dns_name : d->domain_name,
309                                                      sid_string_talloc(state->mem_ctx, &d->sid),
310                                                      get_trust_type_string(d),
311                                                      trust_is_transitive(d) ? "Yes" : "No",
312                                                      trust_is_inbound(d) ? "Yes" : "No",
313                                                      trust_is_outbound(d) ? "Yes" : "No",
314                                                      is_online ? "Online" : "Offline" );
315                 } else {
316                         extra_data = talloc_asprintf(state->mem_ctx, 
317                                                      "%s\n%s\\%s\\%s\\%s\\%s\\%s\\%s\\%s",
318                                                      extra_data,
319                                                      d->domain_name,
320                                                      d->dns_name ? d->dns_name : d->domain_name,
321                                                      sid_string_talloc(state->mem_ctx, &d->sid),
322                                                      get_trust_type_string(d),
323                                                      trust_is_transitive(d) ? "Yes" : "No",
324                                                      trust_is_inbound(d) ? "Yes" : "No",
325                                                      trust_is_outbound(d) ? "Yes" : "No",
326                                                      is_online ? "Online" : "Offline" );
327                 }
328         }
329         
330         extra_data_len = 0;
331         if (extra_data != NULL) {
332                 extra_data_len = strlen(extra_data);
333         }
334
335         if (extra_data_len > 0) {
336                 state->response->extra_data.data = extra_data;
337                 state->response->length += extra_data_len+1;
338         }
339
340         request_ok(state);      
341 done:
342         TALLOC_FREE( dom_list );
343 }
344
345 enum winbindd_result winbindd_dual_list_trusted_domains(struct winbindd_domain *domain,
346                                                         struct winbindd_cli_state *state)
347 {
348         uint32 i, num_domains;
349         char **names, **alt_names;
350         DOM_SID *sids;
351         int extra_data_len = 0;
352         char *extra_data;
353         NTSTATUS result;
354         bool have_own_domain = False;
355
356         DEBUG(3, ("[%5lu]: list trusted domains\n",
357                   (unsigned long)state->pid));
358
359         result = domain->methods->trusted_domains(domain, state->mem_ctx,
360                                                   &num_domains, &names,
361                                                   &alt_names, &sids);
362
363         if (!NT_STATUS_IS_OK(result)) {
364                 DEBUG(3, ("winbindd_dual_list_trusted_domains: trusted_domains returned %s\n",
365                         nt_errstr(result) ));
366                 return WINBINDD_ERROR;
367         }
368
369         extra_data = talloc_strdup(state->mem_ctx, "");
370
371         if (num_domains > 0)
372                 extra_data = talloc_asprintf(
373                         state->mem_ctx, "%s\\%s\\%s",
374                         names[0], alt_names[0] ? alt_names[0] : names[0],
375                         sid_string_talloc(state->mem_ctx, &sids[0]));
376
377         for (i=1; i<num_domains; i++)
378                 extra_data = talloc_asprintf(
379                         state->mem_ctx, "%s\n%s\\%s\\%s",
380                         extra_data, names[i],
381                         alt_names[i] ? alt_names[i] : names[i],
382                         sid_string_talloc(state->mem_ctx, &sids[i]));
383
384         /* add our primary domain */
385         
386         for (i=0; i<num_domains; i++) {
387                 if (strequal(names[i], domain->name)) {
388                         have_own_domain = True;
389                         break;
390                 }
391         }
392
393         if (state->request->data.list_all_domains && !have_own_domain) {
394                 extra_data = talloc_asprintf(
395                         state->mem_ctx, "%s\n%s\\%s\\%s",
396                         extra_data, domain->name,
397                         domain->alt_name ? domain->alt_name : domain->name,
398                         sid_string_talloc(state->mem_ctx, &domain->sid));
399         }
400
401         /* This is a bit excessive, but the extra data sooner or later will be
402            talloc'ed */
403
404         extra_data_len = 0;
405         if (extra_data != NULL) {
406                 extra_data_len = strlen(extra_data);
407         }
408
409         if (extra_data_len > 0) {
410                 state->response->extra_data.data = extra_data;
411                 state->response->length += extra_data_len+1;
412         }
413
414         return WINBINDD_OK;
415 }
416
417 enum winbindd_result winbindd_dual_getdcname(struct winbindd_domain *domain,
418                                              struct winbindd_cli_state *state)
419 {
420         const char *dcname_slash = NULL;
421         const char *p;
422         struct rpc_pipe_client *netlogon_pipe;
423         NTSTATUS result;
424         WERROR werr;
425         unsigned int orig_timeout;
426         struct winbindd_domain *req_domain;
427
428         state->request->domain_name
429                 [sizeof(state->request->domain_name)-1] = '\0';
430
431         DEBUG(3, ("[%5lu]: Get DC name for %s\n", (unsigned long)state->pid,
432                   state->request->domain_name));
433
434         result = cm_connect_netlogon(domain, &netlogon_pipe);
435
436         if (!NT_STATUS_IS_OK(result)) {
437                 DEBUG(1, ("Can't contact the NETLOGON pipe\n"));
438                 return WINBINDD_ERROR;
439         }
440
441         /* This call can take a long time - allow the server to time out.
442            35 seconds should do it. */
443
444         orig_timeout = rpccli_set_timeout(netlogon_pipe, 35000);
445
446         req_domain = find_domain_from_name_noinit(state->request->domain_name);
447         if (req_domain == domain) {
448                 result = rpccli_netr_GetDcName(netlogon_pipe,
449                                                state->mem_ctx,
450                                                domain->dcname,
451                                                state->request->domain_name,
452                                                &dcname_slash,
453                                                &werr);
454         } else {
455                 result = rpccli_netr_GetAnyDCName(netlogon_pipe,
456                                                   state->mem_ctx,
457                                                   domain->dcname,
458                                                   state->request->domain_name,
459                                                   &dcname_slash,
460                                                   &werr);
461         }
462         /* And restore our original timeout. */
463         rpccli_set_timeout(netlogon_pipe, orig_timeout);
464
465         if (!NT_STATUS_IS_OK(result)) {
466                 DEBUG(5,("Error requesting DCname for domain %s: %s\n",
467                         state->request->domain_name, nt_errstr(result)));
468                 return WINBINDD_ERROR;
469         }
470
471         if (!W_ERROR_IS_OK(werr)) {
472                 DEBUG(5, ("Error requesting DCname for domain %s: %s\n",
473                         state->request->domain_name, win_errstr(werr)));
474                 return WINBINDD_ERROR;
475         }
476
477         p = dcname_slash;
478         if (*p == '\\') {
479                 p+=1;
480         }
481         if (*p == '\\') {
482                 p+=1;
483         }
484
485         fstrcpy(state->response->data.dc_name, p);
486         return WINBINDD_OK;
487 }
488
489 /* This is the child-only version of --sequence. It only allows for a single
490  * domain (ie "our" one) to be displayed. */
491
492 enum winbindd_result winbindd_dual_show_sequence(struct winbindd_domain *domain,
493                                                  struct winbindd_cli_state *state)
494 {
495         DEBUG(3, ("[%5lu]: show sequence\n", (unsigned long)state->pid));
496
497         /* Ensure null termination */
498         state->request->domain_name[sizeof(state->request->domain_name)-1]='\0';
499
500         domain->methods->sequence_number(domain, &domain->sequence_number);
501
502         state->response->data.sequence_number =
503                 domain->sequence_number;
504
505         return WINBINDD_OK;
506 }
507
508 struct domain_info_state {
509         struct winbindd_domain *domain;
510         struct winbindd_cli_state *cli;
511         struct winbindd_request ping_request;
512 };
513
514 static void domain_info_done(struct tevent_req *req);
515
516 void winbindd_domain_info(struct winbindd_cli_state *cli)
517 {
518         struct domain_info_state *state;
519         struct winbindd_domain *domain;
520         struct tevent_req *req;
521
522         DEBUG(3, ("[%5lu]: domain_info [%s]\n", (unsigned long)cli->pid,
523                   cli->request->domain_name));
524
525         domain = find_domain_from_name_noinit(cli->request->domain_name);
526
527         if (domain == NULL) {
528                 DEBUG(3, ("Did not find domain [%s]\n",
529                           cli->request->domain_name));
530                 request_error(cli);
531                 return;
532         }
533
534         if (domain->initialized) {
535                 fstrcpy(cli->response->data.domain_info.name,
536                         domain->name);
537                 fstrcpy(cli->response->data.domain_info.alt_name,
538                         domain->alt_name);
539                 sid_to_fstring(cli->response->data.domain_info.sid,
540                                &domain->sid);
541                 cli->response->data.domain_info.native_mode =
542                         domain->native_mode;
543                 cli->response->data.domain_info.active_directory =
544                         domain->active_directory;
545                 cli->response->data.domain_info.primary =
546                         domain->primary;
547                 request_ok(cli);
548                 return;
549         }
550
551         state = talloc_zero(cli->mem_ctx, struct domain_info_state);
552         if (state == NULL) {
553                 DEBUG(0, ("talloc failed\n"));
554                 request_error(cli);
555                 return;
556         }
557
558         state->cli = cli;
559         state->domain = domain;
560         state->ping_request.cmd = WINBINDD_PING;
561
562         /*
563          * Send a ping down. This implicitly initializes the domain.
564          */
565
566         req = wb_domain_request_send(state, winbind_event_context(),
567                                      domain, &state->ping_request);
568         if (req == NULL) {
569                 DEBUG(3, ("wb_domain_request_send failed\n"));
570                 request_error(cli);
571         }
572         tevent_req_set_callback(req, domain_info_done, state);
573 }
574
575 static void domain_info_done(struct tevent_req *req)
576 {
577         struct domain_info_state *state = tevent_req_callback_data(
578                 req, struct domain_info_state);
579         struct winbindd_response *response;
580         int ret, err;
581
582         ret = wb_domain_request_recv(req, req, &response, &err);
583         TALLOC_FREE(req);
584         if (ret == -1) {
585                 DEBUG(10, ("wb_domain_request failed: %s\n", strerror(errno)));
586                 request_error(state->cli);
587                 return;
588         }
589         if (!state->domain->initialized) {
590                 DEBUG(5, ("wb_domain_request did not initialize domain %s\n",
591                           state->domain->name));
592                 request_error(state->cli);
593                 return;
594         }
595
596         fstrcpy(state->cli->response->data.domain_info.name,
597                 state->domain->name);
598         fstrcpy(state->cli->response->data.domain_info.alt_name,
599                 state->domain->alt_name);
600         sid_to_fstring(state->cli->response->data.domain_info.sid,
601                        &state->domain->sid);
602
603         state->cli->response->data.domain_info.native_mode =
604                 state->domain->native_mode;
605         state->cli->response->data.domain_info.active_directory =
606                 state->domain->active_directory;
607         state->cli->response->data.domain_info.primary =
608                 state->domain->primary;
609
610         request_ok(state->cli);
611 }
612
613 /* List various tidbits of information */
614
615 void winbindd_info(struct winbindd_cli_state *state)
616 {
617
618         DEBUG(3, ("[%5lu]: request misc info\n", (unsigned long)state->pid));
619
620         state->response->data.info.winbind_separator = *lp_winbind_separator();
621         fstrcpy(state->response->data.info.samba_version, samba_version_string());
622         request_ok(state);
623 }
624
625 /* Tell the client the current interface version */
626
627 void winbindd_interface_version(struct winbindd_cli_state *state)
628 {
629         DEBUG(3, ("[%5lu]: request interface version\n",
630                   (unsigned long)state->pid));
631         
632         state->response->data.interface_version = WINBIND_INTERFACE_VERSION;
633         request_ok(state);
634 }
635
636 /* What domain are we a member of? */
637
638 void winbindd_domain_name(struct winbindd_cli_state *state)
639 {
640         DEBUG(3, ("[%5lu]: request domain name\n", (unsigned long)state->pid));
641         
642         fstrcpy(state->response->data.domain_name, lp_workgroup());
643         request_ok(state);
644 }
645
646 /* What's my name again? */
647
648 void winbindd_netbios_name(struct winbindd_cli_state *state)
649 {
650         DEBUG(3, ("[%5lu]: request netbios name\n",
651                   (unsigned long)state->pid));
652         
653         fstrcpy(state->response->data.netbios_name, global_myname());
654         request_ok(state);
655 }
656
657 /* Where can I find the privilaged pipe? */
658
659 void winbindd_priv_pipe_dir(struct winbindd_cli_state *state)
660 {
661         char *priv_dir;
662         DEBUG(3, ("[%5lu]: request location of privileged pipe\n",
663                   (unsigned long)state->pid));
664         
665         priv_dir = get_winbind_priv_pipe_dir();
666         state->response->extra_data.data = talloc_move(state->mem_ctx,
667                                                       &priv_dir);
668
669         /* must add one to length to copy the 0 for string termination */
670         state->response->length +=
671                 strlen((char *)state->response->extra_data.data) + 1;
672
673         request_ok(state);
674 }
675