r25260: add trusted domains always to the end of the list.
[samba.git] / source / winbindd / 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_END(_domain_list, domain, struct winbindd_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
635         init_dc_connection(state->domain);
636
637         if (state->continuation != NULL)
638                 state->continuation(state->private_data, True);
639         talloc_destroy(state->mem_ctx);
640 }
641
642 enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
643                                                    struct winbindd_cli_state *state)
644 {
645         /* Ensure null termination */
646         state->request.domain_name
647                 [sizeof(state->request.domain_name)-1]='\0';
648         state->request.data.init_conn.dcname
649                 [sizeof(state->request.data.init_conn.dcname)-1]='\0';
650
651         if (strlen(state->request.data.init_conn.dcname) > 0) {
652                 fstrcpy(domain->dcname, state->request.data.init_conn.dcname);
653         }
654
655         init_dc_connection(domain);
656
657         if (!domain->initialized) {
658                 /* If we return error here we can't do any cached authentication,
659                    but we may be in disconnected mode and can't initialize correctly.
660                    Do what the previous code did and just return without initialization,
661                    once we go online we'll re-initialize.
662                 */
663                 DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization "
664                         "online = %d\n", domain->name, (int)domain->online ));
665         }
666
667         fstrcpy(state->response.data.domain_info.name, domain->name);
668         fstrcpy(state->response.data.domain_info.alt_name, domain->alt_name);
669         fstrcpy(state->response.data.domain_info.sid,
670                 sid_string_static(&domain->sid));
671         
672         state->response.data.domain_info.native_mode
673                 = domain->native_mode;
674         state->response.data.domain_info.active_directory
675                 = domain->active_directory;
676         state->response.data.domain_info.primary
677                 = domain->primary;
678
679         return WINBINDD_OK;
680 }
681
682 /* Look up global info for the winbind daemon */
683 BOOL init_domain_list(void)
684 {
685         struct winbindd_domain *domain;
686         int role = lp_server_role();
687
688         /* Free existing list */
689         free_domain_list();
690
691         /* BUILTIN domain */
692
693         domain = add_trusted_domain("BUILTIN", NULL, &passdb_methods,
694                                     &global_sid_Builtin);
695         if (domain) {
696                 setup_domain_child(domain, &domain->child, NULL);
697         }
698
699         /* Local SAM */
700
701         domain = add_trusted_domain(get_global_sam_name(), NULL,
702                                     &passdb_methods, get_global_sam_sid());
703         if (domain) {
704                 if ( role != ROLE_DOMAIN_MEMBER ) {
705                         domain->primary = True;
706                 }
707                 setup_domain_child(domain, &domain->child, NULL);
708         }
709
710         /* Add ourselves as the first entry. */
711
712         if ( role == ROLE_DOMAIN_MEMBER ) {
713                 DOM_SID our_sid;
714
715                 if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
716                         DEBUG(0, ("Could not fetch our SID - did we join?\n"));
717                         return False;
718                 }
719         
720                 domain = add_trusted_domain( lp_workgroup(), lp_realm(),
721                                              &cache_methods, &our_sid);
722                 if (domain) {
723                         domain->primary = True;
724                         setup_domain_child(domain, &domain->child, NULL);
725                 
726                         /* Even in the parent winbindd we'll need to
727                            talk to the DC, so try and see if we can
728                            contact it. Theoretically this isn't neccessary
729                            as the init_dc_connection() in init_child_recv()
730                            will do this, but we can start detecting the DC
731                            early here. */
732                         set_domain_online_request(domain);
733                 }
734         }
735
736         return True;
737 }
738
739 void check_domain_trusted( const char *name, const DOM_SID *user_sid )
740 {
741         struct winbindd_domain *domain; 
742         DOM_SID dom_sid;
743         uint32 rid;
744         
745         domain = find_domain_from_name_noinit( name );
746         if ( domain )
747                 return; 
748         
749         sid_copy( &dom_sid, user_sid );         
750         if ( !sid_split_rid( &dom_sid, &rid ) )
751                 return;
752         
753         /* add the newly discovered trusted domain */
754
755         domain = add_trusted_domain( name, NULL, &cache_methods, 
756                                      &dom_sid);
757
758         if ( !domain )
759                 return;
760
761         /* assume this is a trust from a one-way transitive 
762            forest trust */
763
764         domain->active_directory = True;
765         domain->domain_flags = DS_DOMAIN_DIRECT_OUTBOUND;
766         domain->domain_type  = DS_DOMAIN_TRUST_TYPE_UPLEVEL;
767         domain->internal = False;
768         domain->online = True;  
769
770         setup_domain_child(domain, &domain->child, NULL);
771
772         wcache_tdc_add_domain( domain );
773
774         return; 
775 }
776
777 /** 
778  * Given a domain name, return the struct winbindd domain info for it 
779  *
780  * @note Do *not* pass lp_workgroup() to this function.  domain_list
781  *       may modify it's value, and free that pointer.  Instead, our local
782  *       domain may be found by calling find_our_domain().
783  *       directly.
784  *
785  *
786  * @return The domain structure for the named domain, if it is working.
787  */
788
789 struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name)
790 {
791         struct winbindd_domain *domain;
792
793         /* Search through list */
794
795         for (domain = domain_list(); domain != NULL; domain = domain->next) {
796                 if (strequal(domain_name, domain->name) ||
797                     (domain->alt_name[0] &&
798                      strequal(domain_name, domain->alt_name))) {
799                         return domain;
800                 }
801         }
802
803         /* Not found */
804
805         return NULL;
806 }
807
808 struct winbindd_domain *find_domain_from_name(const char *domain_name)
809 {
810         struct winbindd_domain *domain;
811
812         domain = find_domain_from_name_noinit(domain_name);
813
814         if (domain == NULL)
815                 return NULL;
816
817         if (!domain->initialized)
818                 init_dc_connection(domain);
819
820         return domain;
821 }
822
823 /* Given a domain sid, return the struct winbindd domain info for it */
824
825 struct winbindd_domain *find_domain_from_sid_noinit(const DOM_SID *sid)
826 {
827         struct winbindd_domain *domain;
828
829         /* Search through list */
830
831         for (domain = domain_list(); domain != NULL; domain = domain->next) {
832                 if (sid_compare_domain(sid, &domain->sid) == 0)
833                         return domain;
834         }
835
836         /* Not found */
837
838         return NULL;
839 }
840
841 /* Given a domain sid, return the struct winbindd domain info for it */
842
843 struct winbindd_domain *find_domain_from_sid(const DOM_SID *sid)
844 {
845         struct winbindd_domain *domain;
846
847         domain = find_domain_from_sid_noinit(sid);
848
849         if (domain == NULL)
850                 return NULL;
851
852         if (!domain->initialized)
853                 init_dc_connection(domain);
854
855         return domain;
856 }
857
858 struct winbindd_domain *find_our_domain(void)
859 {
860         struct winbindd_domain *domain;
861
862         /* Search through list */
863
864         for (domain = domain_list(); domain != NULL; domain = domain->next) {
865                 if (domain->primary)
866                         return domain;
867         }
868
869         smb_panic("Could not find our domain");
870         return NULL;
871 }
872
873 struct winbindd_domain *find_root_domain(void)
874 {
875         struct winbindd_domain *ours = find_our_domain();       
876         
877         if ( !ours )
878                 return NULL;
879         
880         if ( strlen(ours->forest_name) == 0 )
881                 return NULL;
882         
883         return find_domain_from_name( ours->forest_name );
884 }
885
886 struct winbindd_domain *find_builtin_domain(void)
887 {
888         DOM_SID sid;
889         struct winbindd_domain *domain;
890
891         string_to_sid(&sid, "S-1-5-32");
892         domain = find_domain_from_sid(&sid);
893
894         if (domain == NULL) {
895                 smb_panic("Could not find BUILTIN domain");
896         }
897
898         return domain;
899 }
900
901 /* Find the appropriate domain to lookup a name or SID */
902
903 struct winbindd_domain *find_lookup_domain_from_sid(const DOM_SID *sid)
904 {
905         /* SIDs in the S-1-22-{1,2} domain should be handled by our passdb */
906
907         if ( sid_check_is_in_unix_groups(sid) || 
908              sid_check_is_unix_groups(sid) ||
909              sid_check_is_in_unix_users(sid) ||
910              sid_check_is_unix_users(sid) )
911         {
912                 return find_domain_from_sid(get_global_sam_sid());
913         }
914
915         /* A DC can't ask the local smbd for remote SIDs, here winbindd is the
916          * one to contact the external DC's. On member servers the internal
917          * domains are different: These are part of the local SAM. */
918
919         DEBUG(10, ("find_lookup_domain_from_sid(%s)\n",
920                    sid_string_static(sid)));
921
922         if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) {
923                 DEBUG(10, ("calling find_domain_from_sid\n"));
924                 return find_domain_from_sid(sid);
925         }       
926
927         /* On a member server a query for SID or name can always go to our
928          * primary DC. */
929
930         DEBUG(10, ("calling find_our_domain\n"));
931         return find_our_domain();
932 }
933
934 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
935 {
936         if ( strequal(domain_name, unix_users_domain_name() ) ||
937              strequal(domain_name, unix_groups_domain_name() ) )
938         {
939                 return find_domain_from_name_noinit( get_global_sam_name() );
940         }
941
942         if (IS_DC || strequal(domain_name, "BUILTIN") ||
943             strequal(domain_name, get_global_sam_name()))
944                 return find_domain_from_name_noinit(domain_name);
945
946         /* The "Unix User" and "Unix Group" domain our handled by passdb */
947
948         return find_our_domain();
949 }
950
951 /* Lookup a sid in a domain from a name */
952
953 BOOL winbindd_lookup_sid_by_name(TALLOC_CTX *mem_ctx,
954                                  enum winbindd_cmd orig_cmd,
955                                  struct winbindd_domain *domain, 
956                                  const char *domain_name,
957                                  const char *name, DOM_SID *sid, 
958                                  enum lsa_SidType *type)
959 {
960         NTSTATUS result;
961
962         /* Lookup name */
963         result = domain->methods->name_to_sid(domain, mem_ctx, orig_cmd,
964                                               domain_name, name, sid, type);
965
966         /* Return sid and type if lookup successful */
967         if (!NT_STATUS_IS_OK(result)) {
968                 *type = SID_NAME_UNKNOWN;
969         }
970
971         return NT_STATUS_IS_OK(result);
972 }
973
974 /**
975  * @brief Lookup a name in a domain from a sid.
976  *
977  * @param sid Security ID you want to look up.
978  * @param name On success, set to the name corresponding to @p sid.
979  * @param dom_name On success, set to the 'domain name' corresponding to @p sid.
980  * @param type On success, contains the type of name: alias, group or
981  * user.
982  * @retval True if the name exists, in which case @p name and @p type
983  * are set, otherwise False.
984  **/
985 BOOL winbindd_lookup_name_by_sid(TALLOC_CTX *mem_ctx,
986                                  struct winbindd_domain *domain,
987                                  DOM_SID *sid,
988                                  char **dom_name,
989                                  char **name,
990                                  enum lsa_SidType *type)
991 {
992         NTSTATUS result;
993
994         *dom_name = NULL;
995         *name = NULL;
996
997         /* Lookup name */
998
999         result = domain->methods->sid_to_name(domain, mem_ctx, sid, dom_name, name, type);
1000
1001         /* Return name and type if successful */
1002         
1003         if (NT_STATUS_IS_OK(result)) {
1004                 return True;
1005         }
1006
1007         *type = SID_NAME_UNKNOWN;
1008         
1009         return False;
1010 }
1011
1012 /* Free state information held for {set,get,end}{pw,gr}ent() functions */
1013
1014 void free_getent_state(struct getent_state *state)
1015 {
1016         struct getent_state *temp;
1017
1018         /* Iterate over state list */
1019
1020         temp = state;
1021
1022         while(temp != NULL) {
1023                 struct getent_state *next;
1024
1025                 /* Free sam entries then list entry */
1026
1027                 SAFE_FREE(state->sam_entries);
1028                 DLIST_REMOVE(state, state);
1029                 next = temp->next;
1030
1031                 SAFE_FREE(temp);
1032                 temp = next;
1033         }
1034 }
1035
1036 /* Is this a domain which we may assume no DOMAIN\ prefix? */
1037
1038 static BOOL assume_domain(const char *domain)
1039 {
1040         /* never assume the domain on a standalone server */
1041
1042         if ( lp_server_role() == ROLE_STANDALONE )
1043                 return False;
1044
1045         /* domain member servers may possibly assume for the domain name */
1046
1047         if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
1048                 if ( !strequal(lp_workgroup(), domain) )
1049                         return False;
1050
1051                 if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() )
1052                         return True;
1053         } 
1054
1055         /* only left with a domain controller */
1056
1057         if ( strequal(get_global_sam_name(), domain) )  {
1058                 return True;
1059         }
1060         
1061         return False;
1062 }
1063
1064 /* Parse a string of the form DOMAIN\user into a domain and a user */
1065
1066 BOOL parse_domain_user(const char *domuser, fstring domain, fstring user)
1067 {
1068         char *p = strchr(domuser,*lp_winbind_separator());
1069
1070         if ( !p ) {
1071                 fstrcpy(user, domuser);
1072
1073                 if ( assume_domain(lp_workgroup())) {
1074                         fstrcpy(domain, lp_workgroup());
1075                 } else if ((p = strchr(domuser, '@')) != NULL) {
1076                         fstrcpy(domain, "");                    
1077                 } else {
1078                         return False;
1079                 }
1080         } else {
1081                 fstrcpy(user, p+1);
1082                 fstrcpy(domain, domuser);
1083                 domain[PTR_DIFF(p, domuser)] = 0;
1084         }
1085         
1086         strupper_m(domain);
1087         
1088         return True;
1089 }
1090
1091 BOOL parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
1092                               char **domain, char **user)
1093 {
1094         fstring fstr_domain, fstr_user;
1095         if (!parse_domain_user(domuser, fstr_domain, fstr_user)) {
1096                 return False;
1097         }
1098         *domain = talloc_strdup(mem_ctx, fstr_domain);
1099         *user = talloc_strdup(mem_ctx, fstr_user);
1100         return ((*domain != NULL) && (*user != NULL));
1101 }
1102
1103 /* Ensure an incoming username from NSS is fully qualified. Replace the
1104    incoming fstring with DOMAIN <separator> user. Returns the same
1105    values as parse_domain_user() but also replaces the incoming username.
1106    Used to ensure all names are fully qualified within winbindd.
1107    Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
1108    The protocol definitions of auth_crap, chng_pswd_auth_crap
1109    really should be changed to use this instead of doing things
1110    by hand. JRA. */
1111
1112 BOOL canonicalize_username(fstring username_inout, fstring domain, fstring user)
1113 {
1114         if (!parse_domain_user(username_inout, domain, user)) {
1115                 return False;
1116         }
1117         slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
1118                  domain, *lp_winbind_separator(),
1119                  user);
1120         return True;
1121 }
1122
1123 /*
1124     Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
1125     'winbind separator' options.
1126     This means:
1127         - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
1128         lp_workgroup()
1129
1130     If we are a PDC or BDC, and this is for our domain, do likewise.
1131
1132     Also, if omit DOMAIN if 'winbind trusted domains only = true', as the 
1133     username is then unqualified in unix
1134
1135     We always canonicalize as UPPERCASE DOMAIN, lowercase username.
1136 */
1137 void fill_domain_username(fstring name, const char *domain, const char *user, BOOL can_assume)
1138 {
1139         fstring tmp_user;
1140
1141         fstrcpy(tmp_user, user);
1142         strlower_m(tmp_user);
1143
1144         if (can_assume && assume_domain(domain)) {
1145                 strlcpy(name, tmp_user, sizeof(fstring));
1146         } else {
1147                 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
1148                          domain, *lp_winbind_separator(),
1149                          tmp_user);
1150         }
1151 }
1152
1153 /*
1154  * Winbindd socket accessor functions
1155  */
1156
1157 const char *get_winbind_pipe_dir(void) 
1158 {
1159         return lp_parm_const_string(-1, "winbindd", "socket dir", WINBINDD_SOCKET_DIR);
1160 }
1161
1162 char *get_winbind_priv_pipe_dir(void) 
1163 {
1164         return lock_path(WINBINDD_PRIV_SOCKET_SUBDIR);
1165 }
1166
1167 /*
1168  * Client list accessor functions
1169  */
1170
1171 static struct winbindd_cli_state *_client_list;
1172 static int _num_clients;
1173
1174 /* Return list of all connected clients */
1175
1176 struct winbindd_cli_state *winbindd_client_list(void)
1177 {
1178         return _client_list;
1179 }
1180
1181 /* Add a connection to the list */
1182
1183 void winbindd_add_client(struct winbindd_cli_state *cli)
1184 {
1185         DLIST_ADD(_client_list, cli);
1186         _num_clients++;
1187 }
1188
1189 /* Remove a client from the list */
1190
1191 void winbindd_remove_client(struct winbindd_cli_state *cli)
1192 {
1193         DLIST_REMOVE(_client_list, cli);
1194         _num_clients--;
1195 }
1196
1197 /* Close all open clients */
1198
1199 void winbindd_kill_all_clients(void)
1200 {
1201         struct winbindd_cli_state *cl = winbindd_client_list();
1202
1203         DEBUG(10, ("winbindd_kill_all_clients: going postal\n"));
1204
1205         while (cl) {
1206                 struct winbindd_cli_state *next;
1207                 
1208                 next = cl->next;
1209                 winbindd_remove_client(cl);
1210                 cl = next;
1211         }
1212 }
1213
1214 /* Return number of open clients */
1215
1216 int winbindd_num_clients(void)
1217 {
1218         return _num_clients;
1219 }
1220
1221 NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain,
1222                                   TALLOC_CTX *mem_ctx,
1223                                   const DOM_SID *user_sid,
1224                                   uint32 *p_num_groups, DOM_SID **user_sids)
1225 {
1226         NET_USER_INFO_3 *info3 = NULL;
1227         NTSTATUS status = NT_STATUS_NO_MEMORY;
1228         int i;
1229         size_t num_groups = 0;
1230         DOM_SID group_sid, primary_group;
1231         
1232         DEBUG(3,(": lookup_usergroups_cached\n"));
1233         
1234         *user_sids = NULL;
1235         num_groups = 0;
1236         *p_num_groups = 0;
1237
1238         info3 = netsamlogon_cache_get(mem_ctx, user_sid);
1239
1240         if (info3 == NULL) {
1241                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1242         }
1243
1244         if (info3->num_groups == 0) {
1245                 TALLOC_FREE(info3);
1246                 return NT_STATUS_UNSUCCESSFUL;
1247         }
1248         
1249         /* always add the primary group to the sid array */
1250         sid_compose(&primary_group, &info3->dom_sid.sid, info3->user_rid);
1251         
1252         if (!add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups)) {
1253                 TALLOC_FREE(info3);
1254                 return NT_STATUS_NO_MEMORY;
1255         }
1256
1257         for (i=0; i<info3->num_groups; i++) {
1258                 sid_copy(&group_sid, &info3->dom_sid.sid);
1259                 sid_append_rid(&group_sid, info3->gids[i].g_rid);
1260
1261                 if (!add_sid_to_array(mem_ctx, &group_sid, user_sids,
1262                                  &num_groups)) {
1263                         TALLOC_FREE(info3);
1264                         return NT_STATUS_NO_MEMORY;
1265                 }
1266         }
1267
1268         /* Add any Universal groups in the other_sids list */
1269
1270         for (i=0; i<info3->num_other_sids; i++) {
1271                 /* Skip Domain local groups outside our domain.
1272                    We'll get these from the getsidaliases() RPC call. */
1273                 if (info3->other_sids_attrib[i] & SE_GROUP_RESOURCE)
1274                         continue;
1275
1276                 if (!add_sid_to_array(mem_ctx, &info3->other_sids[i].sid,
1277                                       user_sids, &num_groups))
1278                 {
1279                         TALLOC_FREE(info3);
1280                         return NT_STATUS_NO_MEMORY;                     
1281                 }
1282         }
1283         
1284
1285         TALLOC_FREE(info3);
1286         *p_num_groups = num_groups;
1287         status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1288         
1289         DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
1290
1291         return status;
1292 }
1293
1294 /*********************************************************************
1295  We use this to remove spaces from user and group names
1296 ********************************************************************/
1297
1298 void ws_name_replace( char *name, char replace )
1299 {
1300         char replace_char[2] = { 0x0, 0x0 };
1301     
1302         if ( !lp_winbind_normalize_names() || (replace == '\0') ) 
1303                 return;
1304
1305         replace_char[0] = replace;      
1306         all_string_sub( name, " ", replace_char, 0 );
1307
1308         return; 
1309 }
1310
1311 /*********************************************************************
1312  We use this to do the inverse of ws_name_replace()
1313 ********************************************************************/
1314
1315 void ws_name_return( char *name, char replace )
1316 {
1317         char replace_char[2] = { 0x0, 0x0 };
1318     
1319         if ( !lp_winbind_normalize_names() || (replace == '\0') ) 
1320                 return;
1321         
1322         replace_char[0] = replace;      
1323         all_string_sub( name, replace_char, " ", 0 );
1324
1325         return; 
1326 }
1327
1328 /*********************************************************************
1329  ********************************************************************/
1330
1331 BOOL winbindd_can_contact_domain( struct winbindd_domain *domain )
1332 {
1333         /* We can contact the domain if it is our primary domain */
1334
1335         if ( domain->primary )
1336                 return True;
1337
1338         /* Can always contact a domain that is in out forest */
1339
1340         if ( domain->domain_flags & DS_DOMAIN_IN_FOREST )
1341                 return True;    
1342
1343         /* We cannot contact the domain if it is running AD and
1344            we have no inbound trust */
1345
1346         if ( domain->active_directory && 
1347              ((domain->domain_flags&DS_DOMAIN_DIRECT_INBOUND) != DS_DOMAIN_DIRECT_INBOUND) ) 
1348         {
1349                 return False;
1350         }
1351         
1352         /* Assume everything else is ok (probably not true but what
1353            can you do?) */
1354         
1355         return True;    
1356 }
1357
1358 /*********************************************************************
1359  ********************************************************************/
1360
1361 BOOL winbindd_internal_child(struct winbindd_child *child)
1362 {
1363         if ((child == idmap_child()) || (child == locator_child())) {
1364                 return True;
1365         }
1366
1367         return False;
1368 }
1369
1370 #ifdef HAVE_KRB5_LOCATE_PLUGIN_H
1371
1372 /*********************************************************************
1373  ********************************************************************/
1374
1375 static void winbindd_set_locator_kdc_env(const struct winbindd_domain *domain)
1376 {
1377         char *var = NULL;
1378         const char *kdc = NULL;
1379         int lvl = 11;
1380
1381         if (!domain || !domain->alt_name || !*domain->alt_name) {
1382                 return;
1383         }
1384
1385         if (domain->initialized && !domain->active_directory) {
1386                 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s not AD\n",
1387                         domain->alt_name));
1388                 return;
1389         }
1390
1391         kdc = inet_ntoa(domain->dcaddr.sin_addr);
1392         if (!kdc) {
1393                 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC IP\n",
1394                         domain->alt_name));
1395                 kdc = domain->dcname;
1396         }
1397
1398         if (!kdc || !*kdc) {
1399                 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC at all\n",
1400                         domain->alt_name));
1401                 return;
1402         }
1403
1404         if (asprintf(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1405                      strupper_static(domain->alt_name)) == -1) {
1406                 return;
1407         }
1408
1409         DEBUG(lvl,("winbindd_set_locator_kdc_env: setting var: %s to: %s\n",
1410                 var, kdc));
1411
1412         setenv(var, kdc, 1);
1413         free(var);
1414 }
1415
1416 /*********************************************************************
1417  ********************************************************************/
1418
1419 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1420 {
1421         struct winbindd_domain *our_dom = find_our_domain();
1422
1423         winbindd_set_locator_kdc_env(domain);
1424
1425         if (domain != our_dom) {
1426                 winbindd_set_locator_kdc_env(our_dom);
1427         }
1428 }
1429
1430 /*********************************************************************
1431  ********************************************************************/
1432
1433 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1434 {
1435         char *var = NULL;
1436
1437         if (!domain || !domain->alt_name || !*domain->alt_name) {
1438                 return;
1439         }
1440
1441         if (asprintf(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1442                      strupper_static(domain->alt_name)) == -1) {
1443                 return;
1444         }
1445
1446         unsetenv(var);
1447         free(var);
1448 }
1449 #else
1450
1451 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1452 {
1453         return;
1454 }
1455
1456 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1457 {
1458         return;
1459 }
1460
1461 #endif /* HAVE_KRB5_LOCATE_PLUGIN_H */