r25571: split up child_dispatch_table into domain, idmap and locator tables
[samba.git] / source3 / 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
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 && sid) {
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_END(_domain_list, domain, struct winbindd_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                         if (domain) {
328                                 setup_domain_child(domain,
329                                                    &domain->child,
330                                                    domain_dispatch_table,
331                                                    NULL);
332                         }
333                 }
334                 p=q;
335                 if (p != NULL)
336                         p += 1;
337         }
338
339         SAFE_FREE(response->extra_data.data);
340
341         /* 
342            Cases to consider when scanning trusts:
343            (a) we are calling from a child domain (primary && !forest_root)
344            (b) we are calling from the root of the forest (primary && forest_root)
345            (c) we are calling from a trusted forest domain (!primary
346                && !forest_root)
347         */
348
349         if ( state->primary ) {
350                 /* If this is our primary domain and we are not the in the
351                    forest root, we have to scan the root trusts first */
352
353                 if ( !state->forest_root )
354                         rescan_forest_root_trusts();
355                 else
356                         rescan_forest_trusts();
357
358         } else if ( state->forest_root ) {
359                 /* Once we have done root forest trust search, we can
360                    go on to search thing trusted forests */
361
362                 rescan_forest_trusts();
363         }
364         
365         talloc_destroy(state->mem_ctx);
366         
367         return;
368 }
369
370 /********************************************************************
371  Scan the trusts of our forest root
372 ********************************************************************/
373
374 static void rescan_forest_root_trusts( void )
375 {
376         struct winbindd_tdc_domain *dom_list = NULL;
377         size_t num_trusts = 0;
378         int i;  
379
380         /* The only transitive trusts supported by Windows 2003 AD are
381            (a) Parent-Child, (b) Tree-Root, and (c) Forest.   The
382            first two are handled in forest and listed by
383            DsEnumerateDomainTrusts().  Forest trusts are not so we
384            have to do that ourselves. */
385
386         if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
387                 return;
388
389         for ( i=0; i<num_trusts; i++ ) {
390                 struct winbindd_domain *d = NULL;
391
392                 /* Find the forest root.  Don't necessarily trust 
393                    the domain_list() as our primary domain may not 
394                    have been initialized. */
395
396                 if ( !(dom_list[i].trust_flags & DS_DOMAIN_TREE_ROOT) ) {
397                         continue;                       
398                 }
399         
400                 /* Here's the forest root */
401
402                 d = find_domain_from_name_noinit( dom_list[i].domain_name );
403
404                 if ( !d ) {
405                         d = add_trusted_domain( dom_list[i].domain_name,
406                                                 dom_list[i].dns_name,
407                                                 &cache_methods,
408                                                 &dom_list[i].sid );
409                 }
410
411                 DEBUG(10,("rescan_forest_root_trusts: Following trust path "
412                           "for domain tree root %s (%s)\n",
413                           d->name, d->alt_name ));
414
415                 d->domain_flags = dom_list[i].trust_flags;
416                 d->domain_type  = dom_list[i].trust_type;               
417                 d->domain_trust_attribs = dom_list[i].trust_attribs;            
418                 
419                 add_trusted_domains( d );
420
421                 break;          
422         }
423
424         TALLOC_FREE( dom_list );
425
426         return;
427 }
428
429 /********************************************************************
430  scan the transitive forest trists (not our own)
431 ********************************************************************/
432
433
434 static void rescan_forest_trusts( void )
435 {
436         struct winbindd_domain *d = NULL;
437         struct winbindd_tdc_domain *dom_list = NULL;
438         size_t num_trusts = 0;
439         int i;  
440
441         /* The only transitive trusts supported by Windows 2003 AD are
442            (a) Parent-Child, (b) Tree-Root, and (c) Forest.   The
443            first two are handled in forest and listed by
444            DsEnumerateDomainTrusts().  Forest trusts are not so we
445            have to do that ourselves. */
446
447         if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
448                 return;
449
450         for ( i=0; i<num_trusts; i++ ) {
451                 uint32 flags   = dom_list[i].trust_flags;
452                 uint32 type    = dom_list[i].trust_type;
453                 uint32 attribs = dom_list[i].trust_attribs;
454                 
455                 d = find_domain_from_name_noinit( dom_list[i].domain_name );
456
457                 /* ignore our primary and internal domains */
458
459                 if ( d && (d->internal || d->primary ) )
460                         continue;               
461                 
462                 if ( (flags & DS_DOMAIN_DIRECT_INBOUND) &&
463                      (type == DS_DOMAIN_TRUST_TYPE_UPLEVEL) &&
464                      (attribs == DS_DOMAIN_TRUST_ATTRIB_FOREST_TRANSITIVE) )
465                 {
466                         /* add the trusted domain if we don't know
467                            about it */
468
469                         if ( !d ) {
470                                 d = add_trusted_domain( dom_list[i].domain_name,
471                                                         dom_list[i].dns_name,
472                                                         &cache_methods,
473                                                         &dom_list[i].sid );
474                         }
475                         
476                         DEBUG(10,("Following trust path for domain %s (%s)\n",
477                                   d->name, d->alt_name ));
478                         add_trusted_domains( d );
479                 }
480         }
481
482         TALLOC_FREE( dom_list );
483
484         return; 
485 }
486
487 /*********************************************************************
488  The process of updating the trusted domain list is a three step
489  async process:
490  (a) ask our domain
491  (b) ask the root domain in our forest
492  (c) ask the a DC in any Win2003 trusted forests
493 *********************************************************************/
494
495 void rescan_trusted_domains( void )
496 {
497         time_t now = time(NULL);
498         
499         /* see if the time has come... */
500         
501         if ((now >= last_trustdom_scan) &&
502             ((now-last_trustdom_scan) < WINBINDD_RESCAN_FREQ) )
503                 return;
504                 
505         /* clear the TRUSTDOM cache first */
506
507         wcache_tdc_clear();
508
509         /* this will only add new domains we didn't already know about
510            in the domain_list()*/
511         
512         add_trusted_domains( find_our_domain() );
513
514         last_trustdom_scan = now;
515         
516         return; 
517 }
518
519 struct init_child_state {
520         TALLOC_CTX *mem_ctx;
521         struct winbindd_domain *domain;
522         struct winbindd_request *request;
523         struct winbindd_response *response;
524         void (*continuation)(void *private_data, bool success);
525         void *private_data;
526 };
527
528 static void init_child_recv(void *private_data, bool success);
529 static void init_child_getdc_recv(void *private_data, bool success);
530
531 enum winbindd_result init_child_connection(struct winbindd_domain *domain,
532                                            void (*continuation)(void *private_data,
533                                                                 bool success),
534                                            void *private_data)
535 {
536         TALLOC_CTX *mem_ctx;
537         struct winbindd_request *request;
538         struct winbindd_response *response;
539         struct init_child_state *state;
540         struct winbindd_domain *request_domain;
541
542         mem_ctx = talloc_init("init_child_connection");
543         if (mem_ctx == NULL) {
544                 DEBUG(0, ("talloc_init failed\n"));
545                 return WINBINDD_ERROR;
546         }
547
548         request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request);
549         response = TALLOC_P(mem_ctx, struct winbindd_response);
550         state = TALLOC_P(mem_ctx, struct init_child_state);
551
552         if ((request == NULL) || (response == NULL) || (state == NULL)) {
553                 DEBUG(0, ("talloc failed\n"));
554                 TALLOC_FREE(mem_ctx);
555                 continuation(private_data, False);
556                 return WINBINDD_ERROR;
557         }
558
559         request->length = sizeof(*request);
560
561         state->mem_ctx = mem_ctx;
562         state->domain = domain;
563         state->request = request;
564         state->response = response;
565         state->continuation = continuation;
566         state->private_data = private_data;
567
568         if (IS_DC || domain->primary || domain->internal ) {
569                 /* The primary domain has to find the DC name itself */
570                 request->cmd = WINBINDD_INIT_CONNECTION;
571                 fstrcpy(request->domain_name, domain->name);
572                 request->data.init_conn.is_primary = domain->internal ? False : True;
573                 fstrcpy(request->data.init_conn.dcname, "");
574                 async_request(mem_ctx, &domain->child, request, response,
575                               init_child_recv, state);
576                 return WINBINDD_PENDING;
577         }
578
579         /* This is *not* the primary domain, let's ask our DC about a DC
580          * name */
581
582         request->cmd = WINBINDD_GETDCNAME;
583         fstrcpy(request->domain_name, domain->name);
584
585         request_domain = find_our_domain();
586         async_domain_request(mem_ctx, request_domain, request, response,
587                              init_child_getdc_recv, state);
588         return WINBINDD_PENDING;
589 }
590
591 static void init_child_getdc_recv(void *private_data, bool success)
592 {
593         struct init_child_state *state =
594                 talloc_get_type_abort(private_data, struct init_child_state);
595         const char *dcname = "";
596
597         DEBUG(10, ("Received getdcname response\n"));
598
599         if (success && (state->response->result == WINBINDD_OK)) {
600                 dcname = state->response->data.dc_name;
601         }
602
603         state->request->cmd = WINBINDD_INIT_CONNECTION;
604         fstrcpy(state->request->domain_name, state->domain->name);
605         state->request->data.init_conn.is_primary = False;
606         fstrcpy(state->request->data.init_conn.dcname, dcname);
607
608         async_request(state->mem_ctx, &state->domain->child,
609                       state->request, state->response,
610                       init_child_recv, state);
611 }
612
613 static void init_child_recv(void *private_data, bool success)
614 {
615         struct init_child_state *state =
616                 talloc_get_type_abort(private_data, struct init_child_state);
617
618         DEBUG(5, ("Received child initialization response for domain %s\n",
619                   state->domain->name));
620
621         if ((!success) || (state->response->result != WINBINDD_OK)) {
622                 DEBUG(3, ("Could not init child\n"));
623                 state->continuation(state->private_data, False);
624                 talloc_destroy(state->mem_ctx);
625                 return;
626         }
627
628         fstrcpy(state->domain->name,
629                 state->response->data.domain_info.name);
630         fstrcpy(state->domain->alt_name,
631                 state->response->data.domain_info.alt_name);
632         string_to_sid(&state->domain->sid,
633                       state->response->data.domain_info.sid);
634         state->domain->native_mode =
635                 state->response->data.domain_info.native_mode;
636         state->domain->active_directory =
637                 state->response->data.domain_info.active_directory;
638
639         init_dc_connection(state->domain);
640
641         if (state->continuation != NULL)
642                 state->continuation(state->private_data, True);
643         talloc_destroy(state->mem_ctx);
644 }
645
646 enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
647                                                    struct winbindd_cli_state *state)
648 {
649         /* Ensure null termination */
650         state->request.domain_name
651                 [sizeof(state->request.domain_name)-1]='\0';
652         state->request.data.init_conn.dcname
653                 [sizeof(state->request.data.init_conn.dcname)-1]='\0';
654
655         if (strlen(state->request.data.init_conn.dcname) > 0) {
656                 fstrcpy(domain->dcname, state->request.data.init_conn.dcname);
657         }
658
659         init_dc_connection(domain);
660
661         if (!domain->initialized) {
662                 /* If we return error here we can't do any cached authentication,
663                    but we may be in disconnected mode and can't initialize correctly.
664                    Do what the previous code did and just return without initialization,
665                    once we go online we'll re-initialize.
666                 */
667                 DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization "
668                         "online = %d\n", domain->name, (int)domain->online ));
669         }
670
671         fstrcpy(state->response.data.domain_info.name, domain->name);
672         fstrcpy(state->response.data.domain_info.alt_name, domain->alt_name);
673         fstrcpy(state->response.data.domain_info.sid,
674                 sid_string_static(&domain->sid));
675         
676         state->response.data.domain_info.native_mode
677                 = domain->native_mode;
678         state->response.data.domain_info.active_directory
679                 = domain->active_directory;
680         state->response.data.domain_info.primary
681                 = domain->primary;
682
683         return WINBINDD_OK;
684 }
685
686 /* Look up global info for the winbind daemon */
687 bool init_domain_list(void)
688 {
689         struct winbindd_domain *domain;
690         int role = lp_server_role();
691
692         /* Free existing list */
693         free_domain_list();
694
695         /* BUILTIN domain */
696
697         domain = add_trusted_domain("BUILTIN", NULL, &passdb_methods,
698                                     &global_sid_Builtin);
699         if (domain) {
700                 setup_domain_child(domain,
701                                    &domain->child,
702                                    domain_dispatch_table,
703                                    NULL);
704         }
705
706         /* Local SAM */
707
708         domain = add_trusted_domain(get_global_sam_name(), NULL,
709                                     &passdb_methods, get_global_sam_sid());
710         if (domain) {
711                 if ( role != ROLE_DOMAIN_MEMBER ) {
712                         domain->primary = True;
713                 }
714                 setup_domain_child(domain,
715                                    &domain->child,
716                                    domain_dispatch_table,
717                                    NULL);
718         }
719
720         /* Add ourselves as the first entry. */
721
722         if ( role == ROLE_DOMAIN_MEMBER ) {
723                 DOM_SID our_sid;
724
725                 if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
726                         DEBUG(0, ("Could not fetch our SID - did we join?\n"));
727                         return False;
728                 }
729         
730                 domain = add_trusted_domain( lp_workgroup(), lp_realm(),
731                                              &cache_methods, &our_sid);
732                 if (domain) {
733                         domain->primary = True;
734                         setup_domain_child(domain,
735                                            &domain->child,
736                                            domain_dispatch_table,
737                                            NULL);
738
739                         /* Even in the parent winbindd we'll need to
740                            talk to the DC, so try and see if we can
741                            contact it. Theoretically this isn't neccessary
742                            as the init_dc_connection() in init_child_recv()
743                            will do this, but we can start detecting the DC
744                            early here. */
745                         set_domain_online_request(domain);
746                 }
747         }
748
749         return True;
750 }
751
752 void check_domain_trusted( const char *name, const DOM_SID *user_sid )
753 {
754         struct winbindd_domain *domain; 
755         DOM_SID dom_sid;
756         uint32 rid;
757         
758         domain = find_domain_from_name_noinit( name );
759         if ( domain )
760                 return; 
761         
762         sid_copy( &dom_sid, user_sid );         
763         if ( !sid_split_rid( &dom_sid, &rid ) )
764                 return;
765         
766         /* add the newly discovered trusted domain */
767
768         domain = add_trusted_domain( name, NULL, &cache_methods, 
769                                      &dom_sid);
770
771         if ( !domain )
772                 return;
773
774         /* assume this is a trust from a one-way transitive 
775            forest trust */
776
777         domain->active_directory = True;
778         domain->domain_flags = DS_DOMAIN_DIRECT_OUTBOUND;
779         domain->domain_type  = DS_DOMAIN_TRUST_TYPE_UPLEVEL;
780         domain->internal = False;
781         domain->online = True;  
782
783         setup_domain_child(domain,
784                            &domain->child,
785                            domain_dispatch_table,
786                            NULL);
787
788         wcache_tdc_add_domain( domain );
789
790         return; 
791 }
792
793 /** 
794  * Given a domain name, return the struct winbindd domain info for it 
795  *
796  * @note Do *not* pass lp_workgroup() to this function.  domain_list
797  *       may modify it's value, and free that pointer.  Instead, our local
798  *       domain may be found by calling find_our_domain().
799  *       directly.
800  *
801  *
802  * @return The domain structure for the named domain, if it is working.
803  */
804
805 struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name)
806 {
807         struct winbindd_domain *domain;
808
809         /* Search through list */
810
811         for (domain = domain_list(); domain != NULL; domain = domain->next) {
812                 if (strequal(domain_name, domain->name) ||
813                     (domain->alt_name[0] &&
814                      strequal(domain_name, domain->alt_name))) {
815                         return domain;
816                 }
817         }
818
819         /* Not found */
820
821         return NULL;
822 }
823
824 struct winbindd_domain *find_domain_from_name(const char *domain_name)
825 {
826         struct winbindd_domain *domain;
827
828         domain = find_domain_from_name_noinit(domain_name);
829
830         if (domain == NULL)
831                 return NULL;
832
833         if (!domain->initialized)
834                 init_dc_connection(domain);
835
836         return domain;
837 }
838
839 /* Given a domain sid, return the struct winbindd domain info for it */
840
841 struct winbindd_domain *find_domain_from_sid_noinit(const DOM_SID *sid)
842 {
843         struct winbindd_domain *domain;
844
845         /* Search through list */
846
847         for (domain = domain_list(); domain != NULL; domain = domain->next) {
848                 if (sid_compare_domain(sid, &domain->sid) == 0)
849                         return domain;
850         }
851
852         /* Not found */
853
854         return NULL;
855 }
856
857 /* Given a domain sid, return the struct winbindd domain info for it */
858
859 struct winbindd_domain *find_domain_from_sid(const DOM_SID *sid)
860 {
861         struct winbindd_domain *domain;
862
863         domain = find_domain_from_sid_noinit(sid);
864
865         if (domain == NULL)
866                 return NULL;
867
868         if (!domain->initialized)
869                 init_dc_connection(domain);
870
871         return domain;
872 }
873
874 struct winbindd_domain *find_our_domain(void)
875 {
876         struct winbindd_domain *domain;
877
878         /* Search through list */
879
880         for (domain = domain_list(); domain != NULL; domain = domain->next) {
881                 if (domain->primary)
882                         return domain;
883         }
884
885         smb_panic("Could not find our domain");
886         return NULL;
887 }
888
889 struct winbindd_domain *find_root_domain(void)
890 {
891         struct winbindd_domain *ours = find_our_domain();       
892         
893         if ( !ours )
894                 return NULL;
895         
896         if ( strlen(ours->forest_name) == 0 )
897                 return NULL;
898         
899         return find_domain_from_name( ours->forest_name );
900 }
901
902 struct winbindd_domain *find_builtin_domain(void)
903 {
904         DOM_SID sid;
905         struct winbindd_domain *domain;
906
907         string_to_sid(&sid, "S-1-5-32");
908         domain = find_domain_from_sid(&sid);
909
910         if (domain == NULL) {
911                 smb_panic("Could not find BUILTIN domain");
912         }
913
914         return domain;
915 }
916
917 /* Find the appropriate domain to lookup a name or SID */
918
919 struct winbindd_domain *find_lookup_domain_from_sid(const DOM_SID *sid)
920 {
921         /* SIDs in the S-1-22-{1,2} domain should be handled by our passdb */
922
923         if ( sid_check_is_in_unix_groups(sid) || 
924              sid_check_is_unix_groups(sid) ||
925              sid_check_is_in_unix_users(sid) ||
926              sid_check_is_unix_users(sid) )
927         {
928                 return find_domain_from_sid(get_global_sam_sid());
929         }
930
931         /* A DC can't ask the local smbd for remote SIDs, here winbindd is the
932          * one to contact the external DC's. On member servers the internal
933          * domains are different: These are part of the local SAM. */
934
935         DEBUG(10, ("find_lookup_domain_from_sid(%s)\n",
936                    sid_string_static(sid)));
937
938         if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) {
939                 DEBUG(10, ("calling find_domain_from_sid\n"));
940                 return find_domain_from_sid(sid);
941         }       
942
943         /* On a member server a query for SID or name can always go to our
944          * primary DC. */
945
946         DEBUG(10, ("calling find_our_domain\n"));
947         return find_our_domain();
948 }
949
950 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
951 {
952         if ( strequal(domain_name, unix_users_domain_name() ) ||
953              strequal(domain_name, unix_groups_domain_name() ) )
954         {
955                 return find_domain_from_name_noinit( get_global_sam_name() );
956         }
957
958         if (IS_DC || strequal(domain_name, "BUILTIN") ||
959             strequal(domain_name, get_global_sam_name()))
960                 return find_domain_from_name_noinit(domain_name);
961
962         /* The "Unix User" and "Unix Group" domain our handled by passdb */
963
964         return find_our_domain();
965 }
966
967 /* Lookup a sid in a domain from a name */
968
969 bool winbindd_lookup_sid_by_name(TALLOC_CTX *mem_ctx,
970                                  enum winbindd_cmd orig_cmd,
971                                  struct winbindd_domain *domain, 
972                                  const char *domain_name,
973                                  const char *name, DOM_SID *sid, 
974                                  enum lsa_SidType *type)
975 {
976         NTSTATUS result;
977
978         /* Lookup name */
979         result = domain->methods->name_to_sid(domain, mem_ctx, orig_cmd,
980                                               domain_name, name, sid, type);
981
982         /* Return sid and type if lookup successful */
983         if (!NT_STATUS_IS_OK(result)) {
984                 *type = SID_NAME_UNKNOWN;
985         }
986
987         return NT_STATUS_IS_OK(result);
988 }
989
990 /**
991  * @brief Lookup a name in a domain from a sid.
992  *
993  * @param sid Security ID you want to look up.
994  * @param name On success, set to the name corresponding to @p sid.
995  * @param dom_name On success, set to the 'domain name' corresponding to @p sid.
996  * @param type On success, contains the type of name: alias, group or
997  * user.
998  * @retval True if the name exists, in which case @p name and @p type
999  * are set, otherwise False.
1000  **/
1001 bool winbindd_lookup_name_by_sid(TALLOC_CTX *mem_ctx,
1002                                  struct winbindd_domain *domain,
1003                                  DOM_SID *sid,
1004                                  char **dom_name,
1005                                  char **name,
1006                                  enum lsa_SidType *type)
1007 {
1008         NTSTATUS result;
1009
1010         *dom_name = NULL;
1011         *name = NULL;
1012
1013         /* Lookup name */
1014
1015         result = domain->methods->sid_to_name(domain, mem_ctx, sid, dom_name, name, type);
1016
1017         /* Return name and type if successful */
1018         
1019         if (NT_STATUS_IS_OK(result)) {
1020                 return True;
1021         }
1022
1023         *type = SID_NAME_UNKNOWN;
1024         
1025         return False;
1026 }
1027
1028 /* Free state information held for {set,get,end}{pw,gr}ent() functions */
1029
1030 void free_getent_state(struct getent_state *state)
1031 {
1032         struct getent_state *temp;
1033
1034         /* Iterate over state list */
1035
1036         temp = state;
1037
1038         while(temp != NULL) {
1039                 struct getent_state *next;
1040
1041                 /* Free sam entries then list entry */
1042
1043                 SAFE_FREE(state->sam_entries);
1044                 DLIST_REMOVE(state, state);
1045                 next = temp->next;
1046
1047                 SAFE_FREE(temp);
1048                 temp = next;
1049         }
1050 }
1051
1052 /* Is this a domain which we may assume no DOMAIN\ prefix? */
1053
1054 static bool assume_domain(const char *domain)
1055 {
1056         /* never assume the domain on a standalone server */
1057
1058         if ( lp_server_role() == ROLE_STANDALONE )
1059                 return False;
1060
1061         /* domain member servers may possibly assume for the domain name */
1062
1063         if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
1064                 if ( !strequal(lp_workgroup(), domain) )
1065                         return False;
1066
1067                 if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() )
1068                         return True;
1069         } 
1070
1071         /* only left with a domain controller */
1072
1073         if ( strequal(get_global_sam_name(), domain) )  {
1074                 return True;
1075         }
1076         
1077         return False;
1078 }
1079
1080 /* Parse a string of the form DOMAIN\user into a domain and a user */
1081
1082 bool parse_domain_user(const char *domuser, fstring domain, fstring user)
1083 {
1084         char *p = strchr(domuser,*lp_winbind_separator());
1085
1086         if ( !p ) {
1087                 fstrcpy(user, domuser);
1088
1089                 if ( assume_domain(lp_workgroup())) {
1090                         fstrcpy(domain, lp_workgroup());
1091                 } else if ((p = strchr(domuser, '@')) != NULL) {
1092                         fstrcpy(domain, "");                    
1093                 } else {
1094                         return False;
1095                 }
1096         } else {
1097                 fstrcpy(user, p+1);
1098                 fstrcpy(domain, domuser);
1099                 domain[PTR_DIFF(p, domuser)] = 0;
1100         }
1101         
1102         strupper_m(domain);
1103         
1104         return True;
1105 }
1106
1107 bool parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
1108                               char **domain, char **user)
1109 {
1110         fstring fstr_domain, fstr_user;
1111         if (!parse_domain_user(domuser, fstr_domain, fstr_user)) {
1112                 return False;
1113         }
1114         *domain = talloc_strdup(mem_ctx, fstr_domain);
1115         *user = talloc_strdup(mem_ctx, fstr_user);
1116         return ((*domain != NULL) && (*user != NULL));
1117 }
1118
1119 /* Ensure an incoming username from NSS is fully qualified. Replace the
1120    incoming fstring with DOMAIN <separator> user. Returns the same
1121    values as parse_domain_user() but also replaces the incoming username.
1122    Used to ensure all names are fully qualified within winbindd.
1123    Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
1124    The protocol definitions of auth_crap, chng_pswd_auth_crap
1125    really should be changed to use this instead of doing things
1126    by hand. JRA. */
1127
1128 bool canonicalize_username(fstring username_inout, fstring domain, fstring user)
1129 {
1130         if (!parse_domain_user(username_inout, domain, user)) {
1131                 return False;
1132         }
1133         slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
1134                  domain, *lp_winbind_separator(),
1135                  user);
1136         return True;
1137 }
1138
1139 /*
1140     Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
1141     'winbind separator' options.
1142     This means:
1143         - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
1144         lp_workgroup()
1145
1146     If we are a PDC or BDC, and this is for our domain, do likewise.
1147
1148     Also, if omit DOMAIN if 'winbind trusted domains only = true', as the 
1149     username is then unqualified in unix
1150
1151     We always canonicalize as UPPERCASE DOMAIN, lowercase username.
1152 */
1153 void fill_domain_username(fstring name, const char *domain, const char *user, bool can_assume)
1154 {
1155         fstring tmp_user;
1156
1157         fstrcpy(tmp_user, user);
1158         strlower_m(tmp_user);
1159
1160         if (can_assume && assume_domain(domain)) {
1161                 strlcpy(name, tmp_user, sizeof(fstring));
1162         } else {
1163                 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
1164                          domain, *lp_winbind_separator(),
1165                          tmp_user);
1166         }
1167 }
1168
1169 /*
1170  * Winbindd socket accessor functions
1171  */
1172
1173 const char *get_winbind_pipe_dir(void) 
1174 {
1175         return lp_parm_const_string(-1, "winbindd", "socket dir", WINBINDD_SOCKET_DIR);
1176 }
1177
1178 char *get_winbind_priv_pipe_dir(void) 
1179 {
1180         return lock_path(WINBINDD_PRIV_SOCKET_SUBDIR);
1181 }
1182
1183 /* Open the winbindd socket */
1184
1185 static int _winbindd_socket = -1;
1186 static int _winbindd_priv_socket = -1;
1187
1188 int open_winbindd_socket(void)
1189 {
1190         if (_winbindd_socket == -1) {
1191                 _winbindd_socket = create_pipe_sock(
1192                         get_winbind_pipe_dir(), WINBINDD_SOCKET_NAME, 0755);
1193                 DEBUG(10, ("open_winbindd_socket: opened socket fd %d\n",
1194                            _winbindd_socket));
1195         }
1196
1197         return _winbindd_socket;
1198 }
1199
1200 int open_winbindd_priv_socket(void)
1201 {
1202         if (_winbindd_priv_socket == -1) {
1203                 _winbindd_priv_socket = create_pipe_sock(
1204                         get_winbind_priv_pipe_dir(), WINBINDD_SOCKET_NAME, 0750);
1205                 DEBUG(10, ("open_winbindd_priv_socket: opened socket fd %d\n",
1206                            _winbindd_priv_socket));
1207         }
1208
1209         return _winbindd_priv_socket;
1210 }
1211
1212 /* Close the winbindd socket */
1213
1214 void close_winbindd_socket(void)
1215 {
1216         if (_winbindd_socket != -1) {
1217                 DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
1218                            _winbindd_socket));
1219                 close(_winbindd_socket);
1220                 _winbindd_socket = -1;
1221         }
1222         if (_winbindd_priv_socket != -1) {
1223                 DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
1224                            _winbindd_priv_socket));
1225                 close(_winbindd_priv_socket);
1226                 _winbindd_priv_socket = -1;
1227         }
1228 }
1229
1230 /*
1231  * Client list accessor functions
1232  */
1233
1234 static struct winbindd_cli_state *_client_list;
1235 static int _num_clients;
1236
1237 /* Return list of all connected clients */
1238
1239 struct winbindd_cli_state *winbindd_client_list(void)
1240 {
1241         return _client_list;
1242 }
1243
1244 /* Add a connection to the list */
1245
1246 void winbindd_add_client(struct winbindd_cli_state *cli)
1247 {
1248         DLIST_ADD(_client_list, cli);
1249         _num_clients++;
1250 }
1251
1252 /* Remove a client from the list */
1253
1254 void winbindd_remove_client(struct winbindd_cli_state *cli)
1255 {
1256         DLIST_REMOVE(_client_list, cli);
1257         _num_clients--;
1258 }
1259
1260 /* Close all open clients */
1261
1262 void winbindd_kill_all_clients(void)
1263 {
1264         struct winbindd_cli_state *cl = winbindd_client_list();
1265
1266         DEBUG(10, ("winbindd_kill_all_clients: going postal\n"));
1267
1268         while (cl) {
1269                 struct winbindd_cli_state *next;
1270                 
1271                 next = cl->next;
1272                 winbindd_remove_client(cl);
1273                 cl = next;
1274         }
1275 }
1276
1277 /* Return number of open clients */
1278
1279 int winbindd_num_clients(void)
1280 {
1281         return _num_clients;
1282 }
1283
1284 NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain,
1285                                   TALLOC_CTX *mem_ctx,
1286                                   const DOM_SID *user_sid,
1287                                   uint32 *p_num_groups, DOM_SID **user_sids)
1288 {
1289         NET_USER_INFO_3 *info3 = NULL;
1290         NTSTATUS status = NT_STATUS_NO_MEMORY;
1291         int i;
1292         size_t num_groups = 0;
1293         DOM_SID group_sid, primary_group;
1294         
1295         DEBUG(3,(": lookup_usergroups_cached\n"));
1296         
1297         *user_sids = NULL;
1298         num_groups = 0;
1299         *p_num_groups = 0;
1300
1301         info3 = netsamlogon_cache_get(mem_ctx, user_sid);
1302
1303         if (info3 == NULL) {
1304                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1305         }
1306
1307         if (info3->num_groups == 0) {
1308                 TALLOC_FREE(info3);
1309                 return NT_STATUS_UNSUCCESSFUL;
1310         }
1311         
1312         /* always add the primary group to the sid array */
1313         sid_compose(&primary_group, &info3->dom_sid.sid, info3->user_rid);
1314         
1315         if (!add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups)) {
1316                 TALLOC_FREE(info3);
1317                 return NT_STATUS_NO_MEMORY;
1318         }
1319
1320         for (i=0; i<info3->num_groups; i++) {
1321                 sid_copy(&group_sid, &info3->dom_sid.sid);
1322                 sid_append_rid(&group_sid, info3->gids[i].g_rid);
1323
1324                 if (!add_sid_to_array(mem_ctx, &group_sid, user_sids,
1325                                  &num_groups)) {
1326                         TALLOC_FREE(info3);
1327                         return NT_STATUS_NO_MEMORY;
1328                 }
1329         }
1330
1331         /* Add any Universal groups in the other_sids list */
1332
1333         for (i=0; i<info3->num_other_sids; i++) {
1334                 /* Skip Domain local groups outside our domain.
1335                    We'll get these from the getsidaliases() RPC call. */
1336                 if (info3->other_sids_attrib[i] & SE_GROUP_RESOURCE)
1337                         continue;
1338
1339                 if (!add_sid_to_array(mem_ctx, &info3->other_sids[i].sid,
1340                                       user_sids, &num_groups))
1341                 {
1342                         TALLOC_FREE(info3);
1343                         return NT_STATUS_NO_MEMORY;                     
1344                 }
1345         }
1346         
1347
1348         TALLOC_FREE(info3);
1349         *p_num_groups = num_groups;
1350         status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1351         
1352         DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
1353
1354         return status;
1355 }
1356
1357 /*********************************************************************
1358  We use this to remove spaces from user and group names
1359 ********************************************************************/
1360
1361 void ws_name_replace( char *name, char replace )
1362 {
1363         char replace_char[2] = { 0x0, 0x0 };
1364     
1365         if ( !lp_winbind_normalize_names() || (replace == '\0') ) 
1366                 return;
1367
1368         replace_char[0] = replace;      
1369         all_string_sub( name, " ", replace_char, 0 );
1370
1371         return; 
1372 }
1373
1374 /*********************************************************************
1375  We use this to do the inverse of ws_name_replace()
1376 ********************************************************************/
1377
1378 void ws_name_return( char *name, char replace )
1379 {
1380         char replace_char[2] = { 0x0, 0x0 };
1381     
1382         if ( !lp_winbind_normalize_names() || (replace == '\0') ) 
1383                 return;
1384         
1385         replace_char[0] = replace;      
1386         all_string_sub( name, replace_char, " ", 0 );
1387
1388         return; 
1389 }
1390
1391 /*********************************************************************
1392  ********************************************************************/
1393
1394 bool winbindd_can_contact_domain( struct winbindd_domain *domain )
1395 {
1396         /* We can contact the domain if it is our primary domain */
1397
1398         if ( domain->primary )
1399                 return True;
1400
1401         /* Can always contact a domain that is in out forest */
1402
1403         if ( domain->domain_flags & DS_DOMAIN_IN_FOREST )
1404                 return True;    
1405
1406         /* We cannot contact the domain if it is running AD and
1407            we have no inbound trust */
1408
1409         if ( domain->active_directory && 
1410              ((domain->domain_flags&DS_DOMAIN_DIRECT_INBOUND) != DS_DOMAIN_DIRECT_INBOUND) ) 
1411         {
1412                 return False;
1413         }
1414         
1415         /* Assume everything else is ok (probably not true but what
1416            can you do?) */
1417         
1418         return True;    
1419 }
1420
1421 /*********************************************************************
1422  ********************************************************************/
1423
1424 bool winbindd_internal_child(struct winbindd_child *child)
1425 {
1426         if ((child == idmap_child()) || (child == locator_child())) {
1427                 return True;
1428         }
1429
1430         return False;
1431 }
1432
1433 #ifdef HAVE_KRB5_LOCATE_PLUGIN_H
1434
1435 /*********************************************************************
1436  ********************************************************************/
1437
1438 static void winbindd_set_locator_kdc_env(const struct winbindd_domain *domain)
1439 {
1440         char *var = NULL;
1441         const char *kdc = NULL;
1442         int lvl = 11;
1443
1444         if (!domain || !domain->alt_name || !*domain->alt_name) {
1445                 return;
1446         }
1447
1448         if (domain->initialized && !domain->active_directory) {
1449                 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s not AD\n",
1450                         domain->alt_name));
1451                 return;
1452         }
1453
1454         kdc = inet_ntoa(domain->dcaddr.sin_addr);
1455         if (!kdc) {
1456                 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC IP\n",
1457                         domain->alt_name));
1458                 kdc = domain->dcname;
1459         }
1460
1461         if (!kdc || !*kdc) {
1462                 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC at all\n",
1463                         domain->alt_name));
1464                 return;
1465         }
1466
1467         if (asprintf(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1468                      strupper_static(domain->alt_name)) == -1) {
1469                 return;
1470         }
1471
1472         DEBUG(lvl,("winbindd_set_locator_kdc_env: setting var: %s to: %s\n",
1473                 var, kdc));
1474
1475         setenv(var, kdc, 1);
1476         free(var);
1477 }
1478
1479 /*********************************************************************
1480  ********************************************************************/
1481
1482 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1483 {
1484         struct winbindd_domain *our_dom = find_our_domain();
1485
1486         winbindd_set_locator_kdc_env(domain);
1487
1488         if (domain != our_dom) {
1489                 winbindd_set_locator_kdc_env(our_dom);
1490         }
1491 }
1492
1493 /*********************************************************************
1494  ********************************************************************/
1495
1496 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1497 {
1498         char *var = NULL;
1499
1500         if (!domain || !domain->alt_name || !*domain->alt_name) {
1501                 return;
1502         }
1503
1504         if (asprintf(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1505                      strupper_static(domain->alt_name)) == -1) {
1506                 return;
1507         }
1508
1509         unsetenv(var);
1510         free(var);
1511 }
1512 #else
1513
1514 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1515 {
1516         return;
1517 }
1518
1519 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1520 {
1521         return;
1522 }
1523
1524 #endif /* HAVE_KRB5_LOCATE_PLUGIN_H */