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