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