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