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