37d29e1765b8a77c951db3ebc6062af19e607089
[sfrench/samba-autobuild/.git] / source / nsswitch / winbindd_util.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon for ntdom nss module
5
6    Copyright (C) Tim Potter 2000-2001
7    Copyright (C) 2001 by Martin Pool <mbp@samba.org>
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 extern struct winbindd_methods cache_methods;
30 extern struct winbindd_methods passdb_methods;
31
32 /**
33  * @file winbindd_util.c
34  *
35  * Winbind daemon for NT domain authentication nss module.
36  **/
37
38 /* The list of trusted domains.  Note that the list can be deleted and
39    recreated using the init_domain_list() function so pointers to
40    individual winbindd_domain structures cannot be made.  Keep a copy of
41    the domain name instead. */
42
43 static struct winbindd_domain *_domain_list;
44
45 /**
46    When was the last scan of trusted domains done?
47    
48    0 == not ever
49 */
50
51 static time_t last_trustdom_scan;
52
53 struct winbindd_domain *domain_list(void)
54 {
55         /* Initialise list */
56
57         if ((!_domain_list) && (!init_domain_list())) {
58                 smb_panic("Init_domain_list failed");
59         }
60
61         return _domain_list;
62 }
63
64 /* Free all entries in the trusted domain list */
65
66 void free_domain_list(void)
67 {
68         struct winbindd_domain *domain = _domain_list;
69
70         while(domain) {
71                 struct winbindd_domain *next = domain->next;
72                 
73                 DLIST_REMOVE(_domain_list, domain);
74                 SAFE_FREE(domain);
75                 domain = next;
76         }
77 }
78
79 static BOOL is_internal_domain(const DOM_SID *sid)
80 {
81         if (sid == NULL)
82                 return False;
83
84         if ( IS_DC )
85                 return sid_check_is_builtin(sid);
86
87         return (sid_check_is_domain(sid) || sid_check_is_builtin(sid));
88 }
89
90 static BOOL is_in_internal_domain(const DOM_SID *sid)
91 {
92         if (sid == NULL)
93                 return False;
94
95         if ( IS_DC )
96                 return sid_check_is_in_builtin(sid);
97
98         return (sid_check_is_in_our_domain(sid) || sid_check_is_in_builtin(sid));
99 }
100
101
102 /* Add a trusted domain to our list of domains */
103 static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name,
104                                                   struct winbindd_methods *methods,
105                                                   const DOM_SID *sid)
106 {
107         struct winbindd_domain *domain;
108         const char *alternative_name = NULL;
109         
110         /* ignore alt_name if we are not in an AD domain */
111         
112         if ( (lp_security() == SEC_ADS) && alt_name && *alt_name) {
113                 alternative_name = alt_name;
114         }
115         
116         /* We can't call domain_list() as this function is called from
117            init_domain_list() and we'll get stuck in a loop. */
118         for (domain = _domain_list; domain; domain = domain->next) {
119                 if (strequal(domain_name, domain->name) ||
120                     strequal(domain_name, domain->alt_name)) 
121                 {
122                         break;                  
123                 }
124
125                 if (alternative_name && *alternative_name) 
126                 {
127                         if (strequal(alternative_name, domain->name) ||
128                             strequal(alternative_name, domain->alt_name)) 
129                         {
130                                 break;                          
131                         }
132                 }
133
134                 if (sid) 
135                 {
136                         if (is_null_sid(sid)) {
137                                 continue;                               
138                         }
139                                 
140                         if (sid_equal(sid, &domain->sid)) {
141                                 break;                          
142                         }
143                 }
144         }
145         
146         /* See if we found a match.  Check if we need to update the
147            SID. */
148
149         if ( domain && sid) {
150                 if ( sid_equal( &domain->sid, &global_sid_NULL ) )
151                         sid_copy( &domain->sid, sid );
152
153                 return domain;          
154         }       
155         
156         /* Create new domain entry */
157
158         if ((domain = SMB_MALLOC_P(struct winbindd_domain)) == NULL)
159                 return NULL;
160
161         /* Fill in fields */
162         
163         ZERO_STRUCTP(domain);
164
165         /* prioritise the short name */
166         if (strchr_m(domain_name, '.') && alternative_name && *alternative_name) {
167                 fstrcpy(domain->name, alternative_name);
168                 fstrcpy(domain->alt_name, domain_name);
169         } else {
170                 fstrcpy(domain->name, domain_name);
171                 if (alternative_name) {
172                         fstrcpy(domain->alt_name, alternative_name);
173                 }
174         }
175
176         domain->methods = methods;
177         domain->backend = NULL;
178         domain->internal = is_internal_domain(sid);
179         domain->sequence_number = DOM_SEQUENCE_NONE;
180         domain->last_seq_check = 0;
181         domain->initialized = False;
182         domain->online = is_internal_domain(sid);
183         domain->check_online_timeout = 0;
184         if (sid) {
185                 sid_copy(&domain->sid, sid);
186         }
187         
188         /* Link to domain list */
189         DLIST_ADD(_domain_list, domain);
190         
191         wcache_tdc_add_domain( domain );
192         
193         DEBUG(2,("Added domain %s %s %s\n", 
194                  domain->name, domain->alt_name,
195                  &domain->sid?sid_string_static(&domain->sid):""));
196         
197         return domain;
198 }
199
200 /********************************************************************
201   rescan our domains looking for new trusted domains
202 ********************************************************************/
203
204 struct trustdom_state {
205         TALLOC_CTX *mem_ctx;
206         BOOL primary;   
207         BOOL forest_root;       
208         struct winbindd_response *response;
209 };
210
211 static void trustdom_recv(void *private_data, BOOL success);
212 static void rescan_forest_root_trusts( void );
213 static void rescan_forest_trusts( void );
214
215 static void add_trusted_domains( struct winbindd_domain *domain )
216 {
217         TALLOC_CTX *mem_ctx;
218         struct winbindd_request *request;
219         struct winbindd_response *response;
220         uint32 fr_flags = (DS_DOMAIN_TREE_ROOT|DS_DOMAIN_IN_FOREST);    
221
222         struct trustdom_state *state;
223
224         mem_ctx = talloc_init("add_trusted_domains");
225         if (mem_ctx == NULL) {
226                 DEBUG(0, ("talloc_init failed\n"));
227                 return;
228         }
229
230         request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request);
231         response = TALLOC_P(mem_ctx, struct winbindd_response);
232         state = TALLOC_P(mem_ctx, struct trustdom_state);
233
234         if ((request == NULL) || (response == NULL) || (state == NULL)) {
235                 DEBUG(0, ("talloc failed\n"));
236                 talloc_destroy(mem_ctx);
237                 return;
238         }
239
240         state->mem_ctx = mem_ctx;
241         state->response = response;
242
243         /* Flags used to know how to continue the forest trust search */
244
245         state->primary = domain->primary;
246         state->forest_root = ((domain->domain_flags & fr_flags) == fr_flags );
247
248         request->length = sizeof(*request);
249         request->cmd = WINBINDD_LIST_TRUSTDOM;
250
251         async_domain_request(mem_ctx, domain, request, response,
252                              trustdom_recv, state);
253 }
254
255 static void trustdom_recv(void *private_data, BOOL success)
256 {
257         struct trustdom_state *state =
258                 talloc_get_type_abort(private_data, struct trustdom_state);
259         struct winbindd_response *response = state->response;
260         char *p;
261
262         if ((!success) || (response->result != WINBINDD_OK)) {
263                 DEBUG(1, ("Could not receive trustdoms\n"));
264                 talloc_destroy(state->mem_ctx);
265                 return;
266         }
267
268         p = (char *)response->extra_data.data;
269
270         while ((p != NULL) && (*p != '\0')) {
271                 char *q, *sidstr, *alt_name;
272                 DOM_SID sid;
273                 struct winbindd_domain *domain;
274                 char *alternate_name = NULL;
275
276                 alt_name = strchr(p, '\\');
277                 if (alt_name == NULL) {
278                         DEBUG(0, ("Got invalid trustdom response\n"));
279                         break;
280                 }
281
282                 *alt_name = '\0';
283                 alt_name += 1;
284
285                 sidstr = strchr(alt_name, '\\');
286                 if (sidstr == NULL) {
287                         DEBUG(0, ("Got invalid trustdom response\n"));
288                         break;
289                 }
290
291                 *sidstr = '\0';
292                 sidstr += 1;
293
294                 q = strchr(sidstr, '\n');
295                 if (q != NULL)
296                         *q = '\0';
297
298                 if (!string_to_sid(&sid, sidstr)) {
299                         /* Allow NULL sid for sibling domains */
300                         if ( strcmp(sidstr,"S-0-0") == 0) {
301                                 sid_copy( &sid, &global_sid_NULL);                              
302                         } else {                                
303                                 DEBUG(0, ("Got invalid trustdom response\n"));
304                                 break;
305                         }                       
306                 }
307
308                 /* use the real alt_name if we have one, else pass in NULL */
309
310                 if ( !strequal( alt_name, "(null)" ) )
311                         alternate_name = alt_name;
312
313                 /* If we have an existing domain structure, calling
314                    add_trusted_domain() will update the SID if
315                    necessary.  This is important because we need the
316                    SID for sibling domains */
317
318                 if ( find_domain_from_name_noinit(p) != NULL ) {
319                         domain = add_trusted_domain(p, alternate_name,
320                                                     &cache_methods,
321                                                     &sid);
322                 } else {
323                         domain = add_trusted_domain(p, alternate_name,
324                                                     &cache_methods,
325                                                     &sid);
326                         if (domain) {
327                                 setup_domain_child(domain, &domain->child, NULL);
328                         }
329                 }
330                 p=q;
331                 if (p != NULL)
332                         p += 1;
333         }
334
335         SAFE_FREE(response->extra_data.data);
336
337         /* 
338            Cases to consider when scanning trusts:
339            (a) we are calling from a child domain (primary && !forest_root)
340            (b) we are calling from the root of the forest (primary && forest_root)
341            (c) we are calling from a trusted forest domain (!primary
342                && !forest_root)
343         */
344
345         if ( state->primary ) {
346                 /* If this is our primary domain and we are not the in the
347                    forest root, we have to scan the root trusts first */
348
349                 if ( !state->forest_root )
350                         rescan_forest_root_trusts();
351                 else
352                         rescan_forest_trusts();
353
354         } else if ( state->forest_root ) {
355                 /* Once we have done root forest trust search, we can
356                    go on to search thing trusted forests */
357
358                 rescan_forest_trusts();
359         }
360         
361         talloc_destroy(state->mem_ctx);
362         
363         return;
364 }
365
366 /********************************************************************
367  Scan the trusts of our forest root
368 ********************************************************************/
369
370 static void rescan_forest_root_trusts( void )
371 {
372         struct winbindd_tdc_domain *dom_list = NULL;
373         size_t num_trusts = 0;
374         int i;  
375
376         /* The only transitive trusts supported by Windows 2003 AD are
377            (a) Parent-Child, (b) Tree-Root, and (c) Forest.   The
378            first two are handled in forest and listed by
379            DsEnumerateDomainTrusts().  Forest trusts are not so we
380            have to do that ourselves. */
381
382         if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
383                 return;
384
385         for ( i=0; i<num_trusts; i++ ) {
386                 struct winbindd_domain *d = NULL;
387
388                 /* Find the forest root.  Don't necessarily trust 
389                    the domain_list() as our primary domain may not 
390                    have been initialized. */
391
392                 if ( !(dom_list[i].trust_flags & DS_DOMAIN_TREE_ROOT) ) {
393                         continue;                       
394                 }
395         
396                 /* Here's the forest root */
397
398                 d = find_domain_from_name_noinit( dom_list[i].domain_name );
399
400                 if ( !d ) {
401                         d = add_trusted_domain( dom_list[i].domain_name,
402                                                 dom_list[i].dns_name,
403                                                 &cache_methods,
404                                                 &dom_list[i].sid );
405                 }
406
407                 DEBUG(10,("rescan_forest_root_trusts: Following trust path "
408                           "for domain tree root %s (%s)\n",
409                           d->name, d->alt_name ));
410
411                 d->domain_flags = dom_list[i].trust_flags;
412                 d->domain_type  = dom_list[i].trust_type;               
413                 d->domain_trust_attribs = dom_list[i].trust_attribs;            
414                 
415                 add_trusted_domains( d );
416
417                 break;          
418         }
419
420         TALLOC_FREE( dom_list );
421
422         return;
423 }
424
425 /********************************************************************
426  scan the transitive forest trists (not our own)
427 ********************************************************************/
428
429
430 static void rescan_forest_trusts( void )
431 {
432         struct winbindd_domain *d = NULL;
433         struct winbindd_tdc_domain *dom_list = NULL;
434         size_t num_trusts = 0;
435         int i;  
436
437         /* The only transitive trusts supported by Windows 2003 AD are
438            (a) Parent-Child, (b) Tree-Root, and (c) Forest.   The
439            first two are handled in forest and listed by
440            DsEnumerateDomainTrusts().  Forest trusts are not so we
441            have to do that ourselves. */
442
443         if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
444                 return;
445
446         for ( i=0; i<num_trusts; i++ ) {
447                 uint32 flags   = dom_list[i].trust_flags;
448                 uint32 type    = dom_list[i].trust_type;
449                 uint32 attribs = dom_list[i].trust_attribs;
450                 
451                 d = find_domain_from_name_noinit( dom_list[i].domain_name );
452
453                 /* ignore our primary and internal domains */
454
455                 if ( d && (d->internal || d->primary ) )
456                         continue;               
457                 
458                 if ( (flags & DS_DOMAIN_DIRECT_INBOUND) &&
459                      (type == DS_DOMAIN_TRUST_TYPE_UPLEVEL) &&
460                      (attribs == DS_DOMAIN_TRUST_ATTRIB_FOREST_TRANSITIVE) )
461                 {
462                         /* add the trusted domain if we don't know
463                            about it */
464
465                         if ( !d ) {
466                                 d = add_trusted_domain( dom_list[i].domain_name,
467                                                         dom_list[i].dns_name,
468                                                         &cache_methods,
469                                                         &dom_list[i].sid );
470                         }
471                         
472                         DEBUG(10,("Following trust path for domain %s (%s)\n",
473                                   d->name, d->alt_name ));
474                         add_trusted_domains( d );
475                 }
476         }
477
478         TALLOC_FREE( dom_list );
479
480         return; 
481 }
482
483 /*********************************************************************
484  The process of updating the trusted domain list is a three step
485  async process:
486  (a) ask our domain
487  (b) ask the root domain in our forest
488  (c) ask the a DC in any Win2003 trusted forests
489 *********************************************************************/
490
491 void rescan_trusted_domains( void )
492 {
493         time_t now = time(NULL);
494         
495         /* see if the time has come... */
496         
497         if ((now >= last_trustdom_scan) &&
498             ((now-last_trustdom_scan) < WINBINDD_RESCAN_FREQ) )
499                 return;
500                 
501         /* clear the TRUSTDOM cache first */
502
503         wcache_tdc_clear();
504
505         /* this will only add new domains we didn't already know about
506            in the domain_list()*/
507         
508         add_trusted_domains( find_our_domain() );
509
510         last_trustdom_scan = now;
511         
512         return; 
513 }
514
515 struct init_child_state {
516         TALLOC_CTX *mem_ctx;
517         struct winbindd_domain *domain;
518         struct winbindd_request *request;
519         struct winbindd_response *response;
520         void (*continuation)(void *private_data, BOOL success);
521         void *private_data;
522 };
523
524 static void init_child_recv(void *private_data, BOOL success);
525 static void init_child_getdc_recv(void *private_data, BOOL success);
526
527 enum winbindd_result init_child_connection(struct winbindd_domain *domain,
528                                            void (*continuation)(void *private_data,
529                                                                 BOOL success),
530                                            void *private_data)
531 {
532         TALLOC_CTX *mem_ctx;
533         struct winbindd_request *request;
534         struct winbindd_response *response;
535         struct init_child_state *state;
536         struct winbindd_domain *request_domain;
537
538         mem_ctx = talloc_init("init_child_connection");
539         if (mem_ctx == NULL) {
540                 DEBUG(0, ("talloc_init failed\n"));
541                 return WINBINDD_ERROR;
542         }
543
544         request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request);
545         response = TALLOC_P(mem_ctx, struct winbindd_response);
546         state = TALLOC_P(mem_ctx, struct init_child_state);
547
548         if ((request == NULL) || (response == NULL) || (state == NULL)) {
549                 DEBUG(0, ("talloc failed\n"));
550                 TALLOC_FREE(mem_ctx);
551                 continuation(private_data, False);
552                 return WINBINDD_ERROR;
553         }
554
555         request->length = sizeof(*request);
556
557         state->mem_ctx = mem_ctx;
558         state->domain = domain;
559         state->request = request;
560         state->response = response;
561         state->continuation = continuation;
562         state->private_data = private_data;
563
564         if (IS_DC || domain->primary || domain->internal ) {
565                 /* The primary domain has to find the DC name itself */
566                 request->cmd = WINBINDD_INIT_CONNECTION;
567                 fstrcpy(request->domain_name, domain->name);
568                 request->data.init_conn.is_primary = domain->internal ? False : True;
569                 fstrcpy(request->data.init_conn.dcname, "");
570                 async_request(mem_ctx, &domain->child, request, response,
571                               init_child_recv, state);
572                 return WINBINDD_PENDING;
573         }
574
575         /* This is *not* the primary domain, let's ask our DC about a DC
576          * name */
577
578         request->cmd = WINBINDD_GETDCNAME;
579         fstrcpy(request->domain_name, domain->name);
580
581         request_domain = find_our_domain();
582         async_domain_request(mem_ctx, request_domain, request, response,
583                              init_child_getdc_recv, state);
584         return WINBINDD_PENDING;
585 }
586
587 static void init_child_getdc_recv(void *private_data, BOOL success)
588 {
589         struct init_child_state *state =
590                 talloc_get_type_abort(private_data, struct init_child_state);
591         const char *dcname = "";
592
593         DEBUG(10, ("Received getdcname response\n"));
594
595         if (success && (state->response->result == WINBINDD_OK)) {
596                 dcname = state->response->data.dc_name;
597         }
598
599         state->request->cmd = WINBINDD_INIT_CONNECTION;
600         fstrcpy(state->request->domain_name, state->domain->name);
601         state->request->data.init_conn.is_primary = False;
602         fstrcpy(state->request->data.init_conn.dcname, dcname);
603
604         async_request(state->mem_ctx, &state->domain->child,
605                       state->request, state->response,
606                       init_child_recv, state);
607 }
608
609 static void init_child_recv(void *private_data, BOOL success)
610 {
611         struct init_child_state *state =
612                 talloc_get_type_abort(private_data, struct init_child_state);
613
614         DEBUG(5, ("Received child initialization response for domain %s\n",
615                   state->domain->name));
616
617         if ((!success) || (state->response->result != WINBINDD_OK)) {
618                 DEBUG(3, ("Could not init child\n"));
619                 state->continuation(state->private_data, False);
620                 talloc_destroy(state->mem_ctx);
621                 return;
622         }
623
624         fstrcpy(state->domain->name,
625                 state->response->data.domain_info.name);
626         fstrcpy(state->domain->alt_name,
627                 state->response->data.domain_info.alt_name);
628         string_to_sid(&state->domain->sid,
629                       state->response->data.domain_info.sid);
630         state->domain->native_mode =
631                 state->response->data.domain_info.native_mode;
632         state->domain->active_directory =
633                 state->response->data.domain_info.active_directory;
634         state->domain->sequence_number =
635                 state->response->data.domain_info.sequence_number;
636
637         init_dc_connection(state->domain);
638
639         if (state->continuation != NULL)
640                 state->continuation(state->private_data, True);
641         talloc_destroy(state->mem_ctx);
642 }
643
644 enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
645                                                    struct winbindd_cli_state *state)
646 {
647         /* Ensure null termination */
648         state->request.domain_name
649                 [sizeof(state->request.domain_name)-1]='\0';
650         state->request.data.init_conn.dcname
651                 [sizeof(state->request.data.init_conn.dcname)-1]='\0';
652
653         if (strlen(state->request.data.init_conn.dcname) > 0) {
654                 fstrcpy(domain->dcname, state->request.data.init_conn.dcname);
655         }
656
657         init_dc_connection(domain);
658
659         if (!domain->initialized) {
660                 /* If we return error here we can't do any cached authentication,
661                    but we may be in disconnected mode and can't initialize correctly.
662                    Do what the previous code did and just return without initialization,
663                    once we go online we'll re-initialize.
664                 */
665                 DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization "
666                         "online = %d\n", domain->name, (int)domain->online ));
667         }
668
669         fstrcpy(state->response.data.domain_info.name, domain->name);
670         fstrcpy(state->response.data.domain_info.alt_name, domain->alt_name);
671         fstrcpy(state->response.data.domain_info.sid,
672                 sid_string_static(&domain->sid));
673         
674         state->response.data.domain_info.native_mode
675                 = domain->native_mode;
676         state->response.data.domain_info.active_directory
677                 = domain->active_directory;
678         state->response.data.domain_info.primary
679                 = domain->primary;
680         state->response.data.domain_info.sequence_number =
681                 domain->sequence_number;
682
683         return WINBINDD_OK;
684 }
685
686 /* Look up global info for the winbind daemon */
687 BOOL init_domain_list(void)
688 {
689         struct winbindd_domain *domain;
690         int role = lp_server_role();
691
692         /* Free existing list */
693         free_domain_list();
694
695         /* Add ourselves as the first entry. */
696
697         if ( role == ROLE_DOMAIN_MEMBER ) {
698                 DOM_SID our_sid;
699
700                 if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
701                         DEBUG(0, ("Could not fetch our SID - did we join?\n"));
702                         return False;
703                 }
704         
705                 domain = add_trusted_domain( lp_workgroup(), lp_realm(),
706                                              &cache_methods, &our_sid);
707                 if (domain) {
708                         domain->primary = True;
709                         setup_domain_child(domain, &domain->child, NULL);
710                 
711                         /* Even in the parent winbindd we'll need to
712                            talk to the DC, so try and see if we can
713                            contact it. Theoretically this isn't neccessary
714                            as the init_dc_connection() in init_child_recv()
715                            will do this, but we can start detecting the DC
716                            early here. */
717                         set_domain_online_request(domain);
718                 }
719         }
720
721         /* Local SAM */
722
723         domain = add_trusted_domain(get_global_sam_name(), NULL,
724                                     &passdb_methods, get_global_sam_sid());
725         if (domain) {
726                 if ( role != ROLE_DOMAIN_MEMBER ) {
727                         domain->primary = True;
728                 }
729                 setup_domain_child(domain, &domain->child, NULL);
730         }
731
732         /* BUILTIN domain */
733
734         domain = add_trusted_domain("BUILTIN", NULL, &passdb_methods,
735                                     &global_sid_Builtin);
736         if (domain) {
737                 setup_domain_child(domain, &domain->child, NULL);
738         }
739
740         return True;
741 }
742
743 void check_domain_trusted( const char *name, const DOM_SID *user_sid )
744 {
745         struct winbindd_domain *domain; 
746         DOM_SID dom_sid;
747         uint32 rid;
748         
749         domain = find_domain_from_name_noinit( name );
750         if ( domain )
751                 return; 
752         
753         sid_copy( &dom_sid, user_sid );         
754         if ( !sid_split_rid( &dom_sid, &rid ) )
755                 return;
756         
757         /* add the newly discovered trusted domain */
758
759         domain = add_trusted_domain( name, NULL, &cache_methods, 
760                                      &dom_sid);
761
762         if ( !domain )
763                 return;
764
765         /* assume this is a trust from a one-way transitive 
766            forest trust */
767
768         domain->active_directory = True;
769         domain->domain_flags = DS_DOMAIN_DIRECT_OUTBOUND;
770         domain->domain_type  = DS_DOMAIN_TRUST_TYPE_UPLEVEL;
771         domain->internal = False;
772         domain->online = True;  
773
774         setup_domain_child(domain, &domain->child, NULL);
775
776         wcache_tdc_add_domain( domain );
777
778         return; 
779 }
780
781 /** 
782  * Given a domain name, return the struct winbindd domain info for it 
783  *
784  * @note Do *not* pass lp_workgroup() to this function.  domain_list
785  *       may modify it's value, and free that pointer.  Instead, our local
786  *       domain may be found by calling find_our_domain().
787  *       directly.
788  *
789  *
790  * @return The domain structure for the named domain, if it is working.
791  */
792
793 struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name)
794 {
795         struct winbindd_domain *domain;
796
797         /* Search through list */
798
799         for (domain = domain_list(); domain != NULL; domain = domain->next) {
800                 if (strequal(domain_name, domain->name) ||
801                     (domain->alt_name[0] &&
802                      strequal(domain_name, domain->alt_name))) {
803                         return domain;
804                 }
805         }
806
807         /* Not found */
808
809         return NULL;
810 }
811
812 struct winbindd_domain *find_domain_from_name(const char *domain_name)
813 {
814         struct winbindd_domain *domain;
815
816         domain = find_domain_from_name_noinit(domain_name);
817
818         if (domain == NULL)
819                 return NULL;
820
821         if (!domain->initialized)
822                 init_dc_connection(domain);
823
824         return domain;
825 }
826
827 /* Given a domain sid, return the struct winbindd domain info for it */
828
829 struct winbindd_domain *find_domain_from_sid_noinit(const DOM_SID *sid)
830 {
831         struct winbindd_domain *domain;
832
833         /* Search through list */
834
835         for (domain = domain_list(); domain != NULL; domain = domain->next) {
836                 if (sid_compare_domain(sid, &domain->sid) == 0)
837                         return domain;
838         }
839
840         /* Not found */
841
842         return NULL;
843 }
844
845 /* Given a domain sid, return the struct winbindd domain info for it */
846
847 struct winbindd_domain *find_domain_from_sid(const DOM_SID *sid)
848 {
849         struct winbindd_domain *domain;
850
851         domain = find_domain_from_sid_noinit(sid);
852
853         if (domain == NULL)
854                 return NULL;
855
856         if (!domain->initialized)
857                 init_dc_connection(domain);
858
859         return domain;
860 }
861
862 struct winbindd_domain *find_our_domain(void)
863 {
864         struct winbindd_domain *domain;
865
866         /* Search through list */
867
868         for (domain = domain_list(); domain != NULL; domain = domain->next) {
869                 if (domain->primary)
870                         return domain;
871         }
872
873         smb_panic("Could not find our domain");
874         return NULL;
875 }
876
877 struct winbindd_domain *find_root_domain(void)
878 {
879         struct winbindd_domain *ours = find_our_domain();       
880         
881         if ( !ours )
882                 return NULL;
883         
884         if ( strlen(ours->forest_name) == 0 )
885                 return NULL;
886         
887         return find_domain_from_name( ours->forest_name );
888 }
889
890 struct winbindd_domain *find_builtin_domain(void)
891 {
892         DOM_SID sid;
893         struct winbindd_domain *domain;
894
895         string_to_sid(&sid, "S-1-5-32");
896         domain = find_domain_from_sid(&sid);
897
898         if (domain == NULL) {
899                 smb_panic("Could not find BUILTIN domain");
900         }
901
902         return domain;
903 }
904
905 /* Find the appropriate domain to lookup a name or SID */
906
907 struct winbindd_domain *find_lookup_domain_from_sid(const DOM_SID *sid)
908 {
909         /* SIDs in the S-1-22-{1,2} domain should be handled by our passdb */
910
911         if ( sid_check_is_in_unix_groups(sid) || 
912              sid_check_is_unix_groups(sid) ||
913              sid_check_is_in_unix_users(sid) ||
914              sid_check_is_unix_users(sid) )
915         {
916                 return find_domain_from_sid(get_global_sam_sid());
917         }
918
919         /* A DC can't ask the local smbd for remote SIDs, here winbindd is the
920          * one to contact the external DC's. On member servers the internal
921          * domains are different: These are part of the local SAM. */
922
923         DEBUG(10, ("find_lookup_domain_from_sid(%s)\n",
924                    sid_string_static(sid)));
925
926         if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) {
927                 DEBUG(10, ("calling find_domain_from_sid\n"));
928                 return find_domain_from_sid(sid);
929         }       
930
931         /* On a member server a query for SID or name can always go to our
932          * primary DC. */
933
934         DEBUG(10, ("calling find_our_domain\n"));
935         return find_our_domain();
936 }
937
938 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
939 {
940         if ( strequal(domain_name, unix_users_domain_name() ) ||
941              strequal(domain_name, unix_groups_domain_name() ) )
942         {
943                 return find_domain_from_name_noinit( get_global_sam_name() );
944         }
945
946         if (IS_DC || strequal(domain_name, "BUILTIN") ||
947             strequal(domain_name, get_global_sam_name()))
948                 return find_domain_from_name_noinit(domain_name);
949
950         /* The "Unix User" and "Unix Group" domain our handled by passdb */
951
952         return find_our_domain();
953 }
954
955 /* Lookup a sid in a domain from a name */
956
957 BOOL winbindd_lookup_sid_by_name(TALLOC_CTX *mem_ctx,
958                                  enum winbindd_cmd orig_cmd,
959                                  struct winbindd_domain *domain, 
960                                  const char *domain_name,
961                                  const char *name, DOM_SID *sid, 
962                                  enum lsa_SidType *type)
963 {
964         NTSTATUS result;
965
966         /* Lookup name */
967         result = domain->methods->name_to_sid(domain, mem_ctx, orig_cmd,
968                                               domain_name, name, sid, type);
969
970         /* Return sid and type if lookup successful */
971         if (!NT_STATUS_IS_OK(result)) {
972                 *type = SID_NAME_UNKNOWN;
973         }
974
975         return NT_STATUS_IS_OK(result);
976 }
977
978 /**
979  * @brief Lookup a name in a domain from a sid.
980  *
981  * @param sid Security ID you want to look up.
982  * @param name On success, set to the name corresponding to @p sid.
983  * @param dom_name On success, set to the 'domain name' corresponding to @p sid.
984  * @param type On success, contains the type of name: alias, group or
985  * user.
986  * @retval True if the name exists, in which case @p name and @p type
987  * are set, otherwise False.
988  **/
989 BOOL winbindd_lookup_name_by_sid(TALLOC_CTX *mem_ctx,
990                                  struct winbindd_domain *domain,
991                                  DOM_SID *sid,
992                                  char **dom_name,
993                                  char **name,
994                                  enum lsa_SidType *type)
995 {
996         NTSTATUS result;
997
998         *dom_name = NULL;
999         *name = NULL;
1000
1001         /* Lookup name */
1002
1003         result = domain->methods->sid_to_name(domain, mem_ctx, sid, dom_name, name, type);
1004
1005         /* Return name and type if successful */
1006         
1007         if (NT_STATUS_IS_OK(result)) {
1008                 return True;
1009         }
1010
1011         *type = SID_NAME_UNKNOWN;
1012         
1013         return False;
1014 }
1015
1016 /* Free state information held for {set,get,end}{pw,gr}ent() functions */
1017
1018 void free_getent_state(struct getent_state *state)
1019 {
1020         struct getent_state *temp;
1021
1022         /* Iterate over state list */
1023
1024         temp = state;
1025
1026         while(temp != NULL) {
1027                 struct getent_state *next;
1028
1029                 /* Free sam entries then list entry */
1030
1031                 SAFE_FREE(state->sam_entries);
1032                 DLIST_REMOVE(state, state);
1033                 next = temp->next;
1034
1035                 SAFE_FREE(temp);
1036                 temp = next;
1037         }
1038 }
1039
1040 /* Is this a domain which we may assume no DOMAIN\ prefix? */
1041
1042 static BOOL assume_domain(const char *domain)
1043 {
1044         /* never assume the domain on a standalone server */
1045
1046         if ( lp_server_role() == ROLE_STANDALONE )
1047                 return False;
1048
1049         /* domain member servers may possibly assume for the domain name */
1050
1051         if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
1052                 if ( !strequal(lp_workgroup(), domain) )
1053                         return False;
1054
1055                 if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() )
1056                         return True;
1057         } 
1058
1059         /* only left with a domain controller */
1060
1061         if ( strequal(get_global_sam_name(), domain) )  {
1062                 return True;
1063         }
1064         
1065         return False;
1066 }
1067
1068 /* Parse a string of the form DOMAIN\user into a domain and a user */
1069
1070 BOOL parse_domain_user(const char *domuser, fstring domain, fstring user)
1071 {
1072         char *p = strchr(domuser,*lp_winbind_separator());
1073
1074         if ( !p ) {
1075                 fstrcpy(user, domuser);
1076
1077                 if ( assume_domain(lp_workgroup())) {
1078                         fstrcpy(domain, lp_workgroup());
1079                 } else if ((p = strchr(domuser, '@')) != NULL) {
1080                         fstrcpy(domain, "");                    
1081                 } else {
1082                         return False;
1083                 }
1084         } else {
1085                 fstrcpy(user, p+1);
1086                 fstrcpy(domain, domuser);
1087                 domain[PTR_DIFF(p, domuser)] = 0;
1088         }
1089         
1090         strupper_m(domain);
1091         
1092         return True;
1093 }
1094
1095 BOOL parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
1096                               char **domain, char **user)
1097 {
1098         fstring fstr_domain, fstr_user;
1099         if (!parse_domain_user(domuser, fstr_domain, fstr_user)) {
1100                 return False;
1101         }
1102         *domain = talloc_strdup(mem_ctx, fstr_domain);
1103         *user = talloc_strdup(mem_ctx, fstr_user);
1104         return ((*domain != NULL) && (*user != NULL));
1105 }
1106
1107 /* Ensure an incoming username from NSS is fully qualified. Replace the
1108    incoming fstring with DOMAIN <separator> user. Returns the same
1109    values as parse_domain_user() but also replaces the incoming username.
1110    Used to ensure all names are fully qualified within winbindd.
1111    Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
1112    The protocol definitions of auth_crap, chng_pswd_auth_crap
1113    really should be changed to use this instead of doing things
1114    by hand. JRA. */
1115
1116 BOOL canonicalize_username(fstring username_inout, fstring domain, fstring user)
1117 {
1118         if (!parse_domain_user(username_inout, domain, user)) {
1119                 return False;
1120         }
1121         slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
1122                  domain, *lp_winbind_separator(),
1123                  user);
1124         return True;
1125 }
1126
1127 /*
1128     Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
1129     'winbind separator' options.
1130     This means:
1131         - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
1132         lp_workgroup()
1133
1134     If we are a PDC or BDC, and this is for our domain, do likewise.
1135
1136     Also, if omit DOMAIN if 'winbind trusted domains only = true', as the 
1137     username is then unqualified in unix
1138
1139     We always canonicalize as UPPERCASE DOMAIN, lowercase username.
1140 */
1141 void fill_domain_username(fstring name, const char *domain, const char *user, BOOL can_assume)
1142 {
1143         fstring tmp_user;
1144
1145         fstrcpy(tmp_user, user);
1146         strlower_m(tmp_user);
1147
1148         if (can_assume && assume_domain(domain)) {
1149                 strlcpy(name, tmp_user, sizeof(fstring));
1150         } else {
1151                 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
1152                          domain, *lp_winbind_separator(),
1153                          tmp_user);
1154         }
1155 }
1156
1157 /*
1158  * Winbindd socket accessor functions
1159  */
1160
1161 char *get_winbind_priv_pipe_dir(void) 
1162 {
1163         return lock_path(WINBINDD_PRIV_SOCKET_SUBDIR);
1164 }
1165
1166 /*
1167  * Client list accessor functions
1168  */
1169
1170 static struct winbindd_cli_state *_client_list;
1171 static int _num_clients;
1172
1173 /* Return list of all connected clients */
1174
1175 struct winbindd_cli_state *winbindd_client_list(void)
1176 {
1177         return _client_list;
1178 }
1179
1180 /* Add a connection to the list */
1181
1182 void winbindd_add_client(struct winbindd_cli_state *cli)
1183 {
1184         DLIST_ADD(_client_list, cli);
1185         _num_clients++;
1186 }
1187
1188 /* Remove a client from the list */
1189
1190 void winbindd_remove_client(struct winbindd_cli_state *cli)
1191 {
1192         DLIST_REMOVE(_client_list, cli);
1193         _num_clients--;
1194 }
1195
1196 /* Close all open clients */
1197
1198 void winbindd_kill_all_clients(void)
1199 {
1200         struct winbindd_cli_state *cl = winbindd_client_list();
1201
1202         DEBUG(10, ("winbindd_kill_all_clients: going postal\n"));
1203
1204         while (cl) {
1205                 struct winbindd_cli_state *next;
1206                 
1207                 next = cl->next;
1208                 winbindd_remove_client(cl);
1209                 cl = next;
1210         }
1211 }
1212
1213 /* Return number of open clients */
1214
1215 int winbindd_num_clients(void)
1216 {
1217         return _num_clients;
1218 }
1219
1220 NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain,
1221                                   TALLOC_CTX *mem_ctx,
1222                                   const DOM_SID *user_sid,
1223                                   uint32 *p_num_groups, DOM_SID **user_sids)
1224 {
1225         NET_USER_INFO_3 *info3 = NULL;
1226         NTSTATUS status = NT_STATUS_NO_MEMORY;
1227         int i;
1228         size_t num_groups = 0;
1229         DOM_SID group_sid, primary_group;
1230         
1231         DEBUG(3,(": lookup_usergroups_cached\n"));
1232         
1233         *user_sids = NULL;
1234         num_groups = 0;
1235         *p_num_groups = 0;
1236
1237         info3 = netsamlogon_cache_get(mem_ctx, user_sid);
1238
1239         if (info3 == NULL) {
1240                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1241         }
1242
1243         if (info3->num_groups == 0) {
1244                 TALLOC_FREE(info3);
1245                 return NT_STATUS_UNSUCCESSFUL;
1246         }
1247         
1248         /* always add the primary group to the sid array */
1249         sid_compose(&primary_group, &info3->dom_sid.sid, info3->user_rid);
1250         
1251         if (!add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups)) {
1252                 TALLOC_FREE(info3);
1253                 return NT_STATUS_NO_MEMORY;
1254         }
1255
1256         for (i=0; i<info3->num_groups; i++) {
1257                 sid_copy(&group_sid, &info3->dom_sid.sid);
1258                 sid_append_rid(&group_sid, info3->gids[i].g_rid);
1259
1260                 if (!add_sid_to_array(mem_ctx, &group_sid, user_sids,
1261                                  &num_groups)) {
1262                         TALLOC_FREE(info3);
1263                         return NT_STATUS_NO_MEMORY;
1264                 }
1265         }
1266
1267         /* Add any Universal groups in the other_sids list */
1268
1269         for (i=0; i<info3->num_other_sids; i++) {
1270                 /* Skip Domain local groups outside our domain.
1271                    We'll get these from the getsidaliases() RPC call. */
1272                 if (info3->other_sids_attrib[i] & SE_GROUP_RESOURCE)
1273                         continue;
1274
1275                 if (!add_sid_to_array(mem_ctx, &info3->other_sids[i].sid,
1276                                       user_sids, &num_groups))
1277                 {
1278                         TALLOC_FREE(info3);
1279                         return NT_STATUS_NO_MEMORY;                     
1280                 }
1281         }
1282         
1283
1284         TALLOC_FREE(info3);
1285         *p_num_groups = num_groups;
1286         status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1287         
1288         DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
1289
1290         return status;
1291 }
1292
1293 /*********************************************************************
1294  We use this to remove spaces from user and group names
1295 ********************************************************************/
1296
1297 void ws_name_replace( char *name, char replace )
1298 {
1299         char replace_char[2] = { 0x0, 0x0 };
1300     
1301         if ( !lp_winbind_normalize_names() || (replace == '\0') ) 
1302                 return;
1303
1304         replace_char[0] = replace;      
1305         all_string_sub( name, " ", replace_char, 0 );
1306
1307         return; 
1308 }
1309
1310 /*********************************************************************
1311  We use this to do the inverse of ws_name_replace()
1312 ********************************************************************/
1313
1314 void ws_name_return( char *name, char replace )
1315 {
1316         char replace_char[2] = { 0x0, 0x0 };
1317     
1318         if ( !lp_winbind_normalize_names() || (replace == '\0') ) 
1319                 return;
1320         
1321         replace_char[0] = replace;      
1322         all_string_sub( name, replace_char, " ", 0 );
1323
1324         return; 
1325 }
1326
1327 /*********************************************************************
1328  ********************************************************************/
1329
1330 BOOL winbindd_can_contact_domain( struct winbindd_domain *domain )
1331 {
1332         /* We can contact the domain if it is our primary domain */
1333
1334         if ( domain->primary )
1335                 return True;
1336
1337         /* Can always contact a domain that is in out forest */
1338
1339         if ( domain->domain_flags & DS_DOMAIN_IN_FOREST )
1340                 return True;    
1341
1342         /* We cannot contact the domain if it is running AD and
1343            we have no inbound trust */
1344
1345         if ( domain->active_directory && 
1346              ((domain->domain_flags&DS_DOMAIN_DIRECT_INBOUND) != DS_DOMAIN_DIRECT_INBOUND) ) 
1347         {
1348                 return False;
1349         }
1350         
1351         /* Assume everything else is ok (probably not true but what
1352            can you do?) */
1353         
1354         return True;    
1355 }
1356
1357 /*********************************************************************
1358  ********************************************************************/
1359
1360 BOOL winbindd_internal_child(struct winbindd_child *child)
1361 {
1362         if ((child == idmap_child()) || (child == locator_child())) {
1363                 return True;
1364         }
1365
1366         return False;
1367 }
1368
1369 #ifdef HAVE_KRB5_LOCATE_PLUGIN_H
1370
1371 /*********************************************************************
1372  ********************************************************************/
1373
1374 static void winbindd_set_locator_kdc_env(const struct winbindd_domain *domain)
1375 {
1376         char *var = NULL;
1377         const char *kdc = NULL;
1378         int lvl = 11;
1379
1380         if (!domain || !domain->alt_name || !*domain->alt_name) {
1381                 return;
1382         }
1383
1384         if (domain->initialized && !domain->active_directory) {
1385                 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s not AD\n",
1386                         domain->alt_name));
1387                 return;
1388         }
1389
1390         kdc = inet_ntoa(domain->dcaddr.sin_addr);
1391         if (!kdc) {
1392                 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC IP\n",
1393                         domain->alt_name));
1394                 kdc = domain->dcname;
1395         }
1396
1397         if (!kdc || !*kdc) {
1398                 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC at all\n",
1399                         domain->alt_name));
1400                 return;
1401         }
1402
1403         if (asprintf(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1404                      strupper_static(domain->alt_name)) == -1) {
1405                 return;
1406         }
1407
1408         DEBUG(lvl,("winbindd_set_locator_kdc_env: setting var: %s to: %s\n",
1409                 var, kdc));
1410
1411         setenv(var, kdc, 1);
1412         free(var);
1413 }
1414
1415 /*********************************************************************
1416  ********************************************************************/
1417
1418 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1419 {
1420         struct winbindd_domain *our_dom = find_our_domain();
1421
1422         winbindd_set_locator_kdc_env(domain);
1423
1424         if (domain != our_dom) {
1425                 winbindd_set_locator_kdc_env(our_dom);
1426         }
1427 }
1428
1429 /*********************************************************************
1430  ********************************************************************/
1431
1432 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1433 {
1434         char *var = NULL;
1435
1436         if (!domain || !domain->alt_name || !*domain->alt_name) {
1437                 return;
1438         }
1439
1440         if (asprintf(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1441                      strupper_static(domain->alt_name)) == -1) {
1442                 return;
1443         }
1444
1445         unsetenv(var);
1446         free(var);
1447 }
1448 #else
1449
1450 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1451 {
1452         return;
1453 }
1454
1455 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1456 {
1457         return;
1458 }
1459
1460 #endif /* HAVE_KRB5_LOCATE_PLUGIN_H */