Fix winbindd running on a Samba DC,
[tprouty/samba.git] / source / nsswitch / winbindd_util.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon for ntdom nss module
5
6    Copyright (C) Tim Potter 2000-2001
7    Copyright (C) 2001 by Martin Pool <mbp@samba.org>
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25 #include "winbindd.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
29
30 extern struct winbindd_methods cache_methods;
31 extern struct winbindd_methods passdb_methods;
32
33 /**
34  * @file winbindd_util.c
35  *
36  * Winbind daemon for NT domain authentication nss module.
37  **/
38
39
40 /* The list of trusted domains.  Note that the list can be deleted and
41    recreated using the init_domain_list() function so pointers to
42    individual winbindd_domain structures cannot be made.  Keep a copy of
43    the domain name instead. */
44
45 static struct winbindd_domain *_domain_list;
46
47 /**
48    When was the last scan of trusted domains done?
49    
50    0 == not ever
51 */
52
53 static time_t last_trustdom_scan;
54
55 struct winbindd_domain *domain_list(void)
56 {
57         /* Initialise list */
58
59         if ((!_domain_list) && (!init_domain_list())) {
60                 smb_panic("Init_domain_list failed\n");
61         }
62
63         return _domain_list;
64 }
65
66 /* Free all entries in the trusted domain list */
67
68 void free_domain_list(void)
69 {
70         struct winbindd_domain *domain = _domain_list;
71
72         while(domain) {
73                 struct winbindd_domain *next = domain->next;
74                 
75                 DLIST_REMOVE(_domain_list, domain);
76                 SAFE_FREE(domain);
77                 domain = next;
78         }
79 }
80
81 static BOOL is_internal_domain(const DOM_SID *sid)
82 {
83         if (sid == NULL)
84                 return False;
85
86         return (sid_check_is_domain(sid) || sid_check_is_builtin(sid));
87 }
88
89 static BOOL is_in_internal_domain(const DOM_SID *sid)
90 {
91         if (sid == NULL)
92                 return False;
93
94         return (sid_check_is_in_our_domain(sid) || sid_check_is_in_builtin(sid));
95 }
96
97
98 /* Add a trusted domain to our list of domains */
99 static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name,
100                                                   struct winbindd_methods *methods,
101                                                   const DOM_SID *sid)
102 {
103         struct winbindd_domain *domain;
104         const char *alternative_name = NULL;
105         
106         /* ignore alt_name if we are not in an AD domain */
107         
108         if ( (lp_security() == SEC_ADS) && alt_name && *alt_name) {
109                 alternative_name = alt_name;
110         }
111         
112         /* We can't call domain_list() as this function is called from
113            init_domain_list() and we'll get stuck in a loop. */
114         for (domain = _domain_list; domain; domain = domain->next) {
115                 if (strequal(domain_name, domain->name) ||
116                     strequal(domain_name, domain->alt_name)) {
117                         return domain;
118                 }
119                 if (alternative_name && *alternative_name) {
120                         if (strequal(alternative_name, domain->name) ||
121                             strequal(alternative_name, domain->alt_name)) {
122                                 return domain;
123                         }
124                 }
125                 if (sid) {
126                         if (is_null_sid(sid)) {
127                                 
128                         } else if (sid_equal(sid, &domain->sid)) {
129                                 return domain;
130                         }
131                 }
132         }
133         
134         /* Create new domain entry */
135
136         if ((domain = SMB_MALLOC_P(struct winbindd_domain)) == NULL)
137                 return NULL;
138
139         /* Fill in fields */
140         
141         ZERO_STRUCTP(domain);
142
143         /* prioritise the short name */
144         if (strchr_m(domain_name, '.') && alternative_name && *alternative_name) {
145                 fstrcpy(domain->name, alternative_name);
146                 fstrcpy(domain->alt_name, domain_name);
147         } else {
148                 fstrcpy(domain->name, domain_name);
149                 if (alternative_name) {
150                         fstrcpy(domain->alt_name, alternative_name);
151                 }
152         }
153
154         domain->methods = methods;
155         domain->backend = NULL;
156         domain->internal = is_internal_domain(sid);
157         domain->sequence_number = DOM_SEQUENCE_NONE;
158         domain->last_seq_check = 0;
159         domain->initialized = False;
160         domain->online = is_internal_domain(sid);
161         domain->check_online_timeout = 0;
162         if (sid) {
163                 sid_copy(&domain->sid, sid);
164         }
165         
166         /* Link to domain list */
167         DLIST_ADD(_domain_list, domain);
168         
169         DEBUG(2,("Added domain %s %s %s\n", 
170                  domain->name, domain->alt_name,
171                  &domain->sid?sid_string_static(&domain->sid):""));
172         
173         return domain;
174 }
175
176 /********************************************************************
177   rescan our domains looking for new trusted domains
178 ********************************************************************/
179
180 struct trustdom_state {
181         TALLOC_CTX *mem_ctx;
182         struct winbindd_response *response;
183 };
184
185 static void trustdom_recv(void *private_data, BOOL success);
186
187 static void add_trusted_domains( struct winbindd_domain *domain )
188 {
189         TALLOC_CTX *mem_ctx;
190         struct winbindd_request *request;
191         struct winbindd_response *response;
192
193         struct trustdom_state *state;
194
195         mem_ctx = talloc_init("add_trusted_domains");
196         if (mem_ctx == NULL) {
197                 DEBUG(0, ("talloc_init failed\n"));
198                 return;
199         }
200
201         request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request);
202         response = TALLOC_P(mem_ctx, struct winbindd_response);
203         state = TALLOC_P(mem_ctx, struct trustdom_state);
204
205         if ((request == NULL) || (response == NULL) || (state == NULL)) {
206                 DEBUG(0, ("talloc failed\n"));
207                 talloc_destroy(mem_ctx);
208                 return;
209         }
210
211         state->mem_ctx = mem_ctx;
212         state->response = response;
213
214         request->length = sizeof(*request);
215         request->cmd = WINBINDD_LIST_TRUSTDOM;
216
217         async_domain_request(mem_ctx, domain, request, response,
218                              trustdom_recv, state);
219 }
220
221 static void trustdom_recv(void *private_data, BOOL success)
222 {
223         struct trustdom_state *state =
224                 talloc_get_type_abort(private_data, struct trustdom_state);
225         struct winbindd_response *response = state->response;
226         char *p;
227
228         if ((!success) || (response->result != WINBINDD_OK)) {
229                 DEBUG(1, ("Could not receive trustdoms\n"));
230                 talloc_destroy(state->mem_ctx);
231                 return;
232         }
233
234         p = (char *)response->extra_data.data;
235
236         while ((p != NULL) && (*p != '\0')) {
237                 char *q, *sidstr, *alt_name;
238                 DOM_SID sid;
239
240                 alt_name = strchr(p, '\\');
241                 if (alt_name == NULL) {
242                         DEBUG(0, ("Got invalid trustdom response\n"));
243                         break;
244                 }
245
246                 *alt_name = '\0';
247                 alt_name += 1;
248
249                 sidstr = strchr(alt_name, '\\');
250                 if (sidstr == NULL) {
251                         DEBUG(0, ("Got invalid trustdom response\n"));
252                         break;
253                 }
254
255                 *sidstr = '\0';
256                 sidstr += 1;
257
258                 q = strchr(sidstr, '\n');
259                 if (q != NULL)
260                         *q = '\0';
261
262                 if (!string_to_sid(&sid, sidstr)) {
263                         /* Allow NULL sid for sibling domains */
264                         if ( strcmp(sidstr,"S-0-0") == 0) {
265                                 sid_copy( &sid, &global_sid_NULL);                              
266                         } else {                                
267                                 DEBUG(0, ("Got invalid trustdom response\n"));
268                                 break;
269                         }                       
270                 }
271
272                 if (find_domain_from_name_noinit(p) == NULL) {
273                         struct winbindd_domain *domain;
274                         char *alternate_name = NULL;
275                         
276                         /* use the real alt_name if we have one, else pass in NULL */
277
278                         if ( !strequal( alt_name, "(null)" ) )
279                                 alternate_name = alt_name;
280
281                         domain = add_trusted_domain(p, alternate_name,
282                                                     &cache_methods,
283                                                     &sid);
284                         setup_domain_child(domain, &domain->child, NULL);
285                 }
286                 p=q;
287                 if (p != NULL)
288                         p += 1;
289         }
290
291         SAFE_FREE(response->extra_data.data);
292         talloc_destroy(state->mem_ctx);
293 }
294
295 /********************************************************************
296  Periodically we need to refresh the trusted domain cache for smbd 
297 ********************************************************************/
298
299 void rescan_trusted_domains( void )
300 {
301         time_t now = time(NULL);
302         
303         /* see if the time has come... */
304         
305         if ((now >= last_trustdom_scan) &&
306             ((now-last_trustdom_scan) < WINBINDD_RESCAN_FREQ) )
307                 return;
308                 
309         /* this will only add new domains we didn't already know about */
310         
311         add_trusted_domains( find_our_domain() );
312
313         last_trustdom_scan = now;
314         
315         return; 
316 }
317
318 struct init_child_state {
319         TALLOC_CTX *mem_ctx;
320         struct winbindd_domain *domain;
321         struct winbindd_request *request;
322         struct winbindd_response *response;
323         void (*continuation)(void *private_data, BOOL success);
324         void *private_data;
325 };
326
327 static void init_child_recv(void *private_data, BOOL success);
328 static void init_child_getdc_recv(void *private_data, BOOL success);
329
330 enum winbindd_result init_child_connection(struct winbindd_domain *domain,
331                                            void (*continuation)(void *private_data,
332                                                                 BOOL success),
333                                            void *private_data)
334 {
335         TALLOC_CTX *mem_ctx;
336         struct winbindd_request *request;
337         struct winbindd_response *response;
338         struct init_child_state *state;
339         struct winbindd_domain *request_domain;
340
341         mem_ctx = talloc_init("init_child_connection");
342         if (mem_ctx == NULL) {
343                 DEBUG(0, ("talloc_init failed\n"));
344                 return WINBINDD_ERROR;
345         }
346
347         request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request);
348         response = TALLOC_P(mem_ctx, struct winbindd_response);
349         state = TALLOC_P(mem_ctx, struct init_child_state);
350
351         if ((request == NULL) || (response == NULL) || (state == NULL)) {
352                 DEBUG(0, ("talloc failed\n"));
353                 TALLOC_FREE(mem_ctx);
354                 continuation(private_data, False);
355                 return WINBINDD_ERROR;
356         }
357
358         request->length = sizeof(*request);
359
360         state->mem_ctx = mem_ctx;
361         state->domain = domain;
362         state->request = request;
363         state->response = response;
364         state->continuation = continuation;
365         state->private_data = private_data;
366
367         if (IS_DC || domain->primary) {
368                 /* The primary domain has to find the DC name itself */
369                 request->cmd = WINBINDD_INIT_CONNECTION;
370                 fstrcpy(request->domain_name, domain->name);
371                 request->data.init_conn.is_primary = True;
372                 fstrcpy(request->data.init_conn.dcname, "");
373                 async_request(mem_ctx, &domain->child, request, response,
374                               init_child_recv, state);
375                 return WINBINDD_PENDING;
376         }
377
378         /* This is *not* the primary domain, let's ask our DC about a DC
379          * name */
380
381         request->cmd = WINBINDD_GETDCNAME;
382         fstrcpy(request->domain_name, domain->name);
383
384         request_domain = find_our_domain();
385         
386         async_domain_request(mem_ctx, request_domain, request, response,
387                              init_child_getdc_recv, state);
388         return WINBINDD_PENDING;
389 }
390
391 static void init_child_getdc_recv(void *private_data, BOOL success)
392 {
393         struct init_child_state *state =
394                 talloc_get_type_abort(private_data, struct init_child_state);
395         const char *dcname = "";
396
397         DEBUG(10, ("Received getdcname response\n"));
398
399         if (success && (state->response->result == WINBINDD_OK)) {
400                 dcname = state->response->data.dc_name;
401         }
402
403         state->request->cmd = WINBINDD_INIT_CONNECTION;
404         fstrcpy(state->request->domain_name, state->domain->name);
405         state->request->data.init_conn.is_primary = False;
406         fstrcpy(state->request->data.init_conn.dcname, dcname);
407
408         async_request(state->mem_ctx, &state->domain->child,
409                       state->request, state->response,
410                       init_child_recv, state);
411 }
412
413 static void init_child_recv(void *private_data, BOOL success)
414 {
415         struct init_child_state *state =
416                 talloc_get_type_abort(private_data, struct init_child_state);
417
418         DEBUG(5, ("Received child initialization response for domain %s\n",
419                   state->domain->name));
420
421         if ((!success) || (state->response->result != WINBINDD_OK)) {
422                 DEBUG(3, ("Could not init child\n"));
423                 state->continuation(state->private_data, False);
424                 talloc_destroy(state->mem_ctx);
425                 return;
426         }
427
428         fstrcpy(state->domain->name,
429                 state->response->data.domain_info.name);
430         fstrcpy(state->domain->alt_name,
431                 state->response->data.domain_info.alt_name);
432         string_to_sid(&state->domain->sid,
433                       state->response->data.domain_info.sid);
434         state->domain->native_mode =
435                 state->response->data.domain_info.native_mode;
436         state->domain->active_directory =
437                 state->response->data.domain_info.active_directory;
438         state->domain->sequence_number =
439                 state->response->data.domain_info.sequence_number;
440
441         init_dc_connection(state->domain);
442
443         if (state->continuation != NULL)
444                 state->continuation(state->private_data, True);
445         talloc_destroy(state->mem_ctx);
446 }
447
448 enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
449                                                    struct winbindd_cli_state *state)
450 {
451         /* Ensure null termination */
452         state->request.domain_name
453                 [sizeof(state->request.domain_name)-1]='\0';
454         state->request.data.init_conn.dcname
455                 [sizeof(state->request.data.init_conn.dcname)-1]='\0';
456
457         if (strlen(state->request.data.init_conn.dcname) > 0) {
458                 fstrcpy(domain->dcname, state->request.data.init_conn.dcname);
459         }
460
461         init_dc_connection(domain);
462
463         if (!domain->initialized) {
464                 /* If we return error here we can't do any cached authentication,
465                    but we may be in disconnected mode and can't initialize correctly.
466                    Do what the previous code did and just return without initialization,
467                    once we go online we'll re-initialize.
468                 */
469                 DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization "
470                         "online = %d\n", domain->name, (int)domain->online ));
471         }
472
473         fstrcpy(state->response.data.domain_info.name, domain->name);
474         fstrcpy(state->response.data.domain_info.alt_name, domain->alt_name);
475         fstrcpy(state->response.data.domain_info.sid,
476                 sid_string_static(&domain->sid));
477         
478         state->response.data.domain_info.native_mode
479                 = domain->native_mode;
480         state->response.data.domain_info.active_directory
481                 = domain->active_directory;
482         state->response.data.domain_info.primary
483                 = domain->primary;
484         state->response.data.domain_info.sequence_number =
485                 domain->sequence_number;
486
487         return WINBINDD_OK;
488 }
489
490 /* Look up global info for the winbind daemon */
491 BOOL init_domain_list(void)
492 {
493         struct winbindd_domain *domain;
494         int role = lp_server_role();
495
496         /* Free existing list */
497         free_domain_list();
498
499         /* Add ourselves as the first entry. */
500
501         if ( role == ROLE_DOMAIN_MEMBER ) {
502                 DOM_SID our_sid;
503
504                 if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
505                         DEBUG(0, ("Could not fetch our SID - did we join?\n"));
506                         return False;
507                 }
508         
509                 domain = add_trusted_domain( lp_workgroup(), lp_realm(),
510                                              &cache_methods, &our_sid);
511                 domain->primary = True;
512                 setup_domain_child(domain, &domain->child, NULL);
513                 
514                 /* Even in the parent winbindd we'll need to
515                    talk to the DC, so try and see if we can
516                    contact it. Theoretically this isn't neccessary
517                    as the init_dc_connection() in init_child_recv()
518                    will do this, but we can start detecting the DC
519                    early here. */
520                 set_domain_online_request(domain);
521         }
522
523         /* Local SAM */
524
525         domain = add_trusted_domain(get_global_sam_name(), NULL,
526                                     &passdb_methods, get_global_sam_sid());
527         if ( role != ROLE_DOMAIN_MEMBER ) {
528                 domain->primary = True;
529         }
530         setup_domain_child(domain, &domain->child, NULL);
531
532         /* BUILTIN domain */
533
534         domain = add_trusted_domain("BUILTIN", NULL, &passdb_methods,
535                                     &global_sid_Builtin);
536         setup_domain_child(domain, &domain->child, NULL);
537
538         return True;
539 }
540
541 /** 
542  * Given a domain name, return the struct winbindd domain info for it 
543  *
544  * @note Do *not* pass lp_workgroup() to this function.  domain_list
545  *       may modify it's value, and free that pointer.  Instead, our local
546  *       domain may be found by calling find_our_domain().
547  *       directly.
548  *
549  *
550  * @return The domain structure for the named domain, if it is working.
551  */
552
553 struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name)
554 {
555         struct winbindd_domain *domain;
556
557         /* Search through list */
558
559         for (domain = domain_list(); domain != NULL; domain = domain->next) {
560                 if (strequal(domain_name, domain->name) ||
561                     (domain->alt_name[0] &&
562                      strequal(domain_name, domain->alt_name))) {
563                         return domain;
564                 }
565         }
566
567         /* Not found */
568
569         return NULL;
570 }
571
572 struct winbindd_domain *find_domain_from_name(const char *domain_name)
573 {
574         struct winbindd_domain *domain;
575
576         domain = find_domain_from_name_noinit(domain_name);
577
578         if (domain == NULL)
579                 return NULL;
580
581         if (!domain->initialized)
582                 init_dc_connection(domain);
583
584         return domain;
585 }
586
587 /* Given a domain sid, return the struct winbindd domain info for it */
588
589 struct winbindd_domain *find_domain_from_sid_noinit(const DOM_SID *sid)
590 {
591         struct winbindd_domain *domain;
592
593         /* Search through list */
594
595         for (domain = domain_list(); domain != NULL; domain = domain->next) {
596                 if (sid_compare_domain(sid, &domain->sid) == 0)
597                         return domain;
598         }
599
600         /* Not found */
601
602         return NULL;
603 }
604
605 /* Given a domain sid, return the struct winbindd domain info for it */
606
607 struct winbindd_domain *find_domain_from_sid(const DOM_SID *sid)
608 {
609         struct winbindd_domain *domain;
610
611         domain = find_domain_from_sid_noinit(sid);
612
613         if (domain == NULL)
614                 return NULL;
615
616         if (!domain->initialized)
617                 init_dc_connection(domain);
618
619         return domain;
620 }
621
622 struct winbindd_domain *find_our_domain(void)
623 {
624         struct winbindd_domain *domain;
625
626         /* Search through list */
627
628         for (domain = domain_list(); domain != NULL; domain = domain->next) {
629                 if (domain->primary)
630                         return domain;
631         }
632
633         smb_panic("Could not find our domain\n");
634         return NULL;
635 }
636
637 struct winbindd_domain *find_root_domain(void)
638 {
639         struct winbindd_domain *ours = find_our_domain();       
640         
641         if ( !ours )
642                 return NULL;
643         
644         if ( strlen(ours->forest_name) == 0 )
645                 return NULL;
646         
647         return find_domain_from_name( ours->forest_name );
648 }
649
650 struct winbindd_domain *find_builtin_domain(void)
651 {
652         DOM_SID sid;
653         struct winbindd_domain *domain;
654
655         string_to_sid(&sid, "S-1-5-32");
656         domain = find_domain_from_sid(&sid);
657
658         if (domain == NULL)
659                 smb_panic("Could not find BUILTIN domain\n");
660
661         return domain;
662 }
663
664 /* Find the appropriate domain to lookup a name or SID */
665
666 struct winbindd_domain *find_lookup_domain_from_sid(const DOM_SID *sid)
667 {
668         /* A DC can't ask the local smbd for remote SIDs, here winbindd is the
669          * one to contact the external DC's. On member servers the internal
670          * domains are different: These are part of the local SAM. */
671
672         DEBUG(10, ("find_lookup_domain_from_sid(%s)\n",
673                    sid_string_static(sid)));
674
675         if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) {
676                 DEBUG(10, ("calling find_domain_from_sid\n"));
677                 return find_domain_from_sid(sid);
678         }
679
680         /* On a member server a query for SID or name can always go to our
681          * primary DC. */
682
683         DEBUG(10, ("calling find_our_domain\n"));
684         return find_our_domain();
685 }
686
687 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
688 {
689         if (IS_DC || strequal(domain_name, "BUILTIN") ||
690             strequal(domain_name, get_global_sam_name()))
691                 return find_domain_from_name_noinit(domain_name);
692
693         return find_our_domain();
694 }
695
696 /* Lookup a sid in a domain from a name */
697
698 BOOL winbindd_lookup_sid_by_name(TALLOC_CTX *mem_ctx,
699                                  struct winbindd_domain *domain, 
700                                  const char *domain_name,
701                                  const char *name, DOM_SID *sid, 
702                                  enum lsa_SidType *type)
703 {
704         NTSTATUS result;
705
706         /* Lookup name */
707         result = domain->methods->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
708
709         /* Return sid and type if lookup successful */
710         if (!NT_STATUS_IS_OK(result)) {
711                 *type = SID_NAME_UNKNOWN;
712         }
713
714         return NT_STATUS_IS_OK(result);
715 }
716
717 /**
718  * @brief Lookup a name in a domain from a sid.
719  *
720  * @param sid Security ID you want to look up.
721  * @param name On success, set to the name corresponding to @p sid.
722  * @param dom_name On success, set to the 'domain name' corresponding to @p sid.
723  * @param type On success, contains the type of name: alias, group or
724  * user.
725  * @retval True if the name exists, in which case @p name and @p type
726  * are set, otherwise False.
727  **/
728 BOOL winbindd_lookup_name_by_sid(TALLOC_CTX *mem_ctx,
729                                  DOM_SID *sid,
730                                  char **dom_name,
731                                  char **name,
732                                  enum lsa_SidType *type)
733 {
734         NTSTATUS result;
735         struct winbindd_domain *domain;
736
737         *dom_name = NULL;
738         *name = NULL;
739
740         domain = find_lookup_domain_from_sid(sid);
741
742         if (!domain) {
743                 DEBUG(1,("Can't find domain from sid\n"));
744                 return False;
745         }
746
747         /* Lookup name */
748
749         result = domain->methods->sid_to_name(domain, mem_ctx, sid, dom_name, name, type);
750
751         /* Return name and type if successful */
752         
753         if (NT_STATUS_IS_OK(result)) {
754                 return True;
755         }
756
757         *type = SID_NAME_UNKNOWN;
758         
759         return False;
760 }
761
762 /* Free state information held for {set,get,end}{pw,gr}ent() functions */
763
764 void free_getent_state(struct getent_state *state)
765 {
766         struct getent_state *temp;
767
768         /* Iterate over state list */
769
770         temp = state;
771
772         while(temp != NULL) {
773                 struct getent_state *next;
774
775                 /* Free sam entries then list entry */
776
777                 SAFE_FREE(state->sam_entries);
778                 DLIST_REMOVE(state, state);
779                 next = temp->next;
780
781                 SAFE_FREE(temp);
782                 temp = next;
783         }
784 }
785
786 /* Is this a domain which we may assume no DOMAIN\ prefix? */
787
788 static BOOL assume_domain(const char *domain)
789 {
790         /* never assume the domain on a standalone server */
791
792         if ( lp_server_role() == ROLE_STANDALONE )
793                 return False;
794
795         /* domain member servers may possibly assume for the domain name */
796
797         if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
798                 if ( !strequal(lp_workgroup(), domain) )
799                         return False;
800
801                 if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() )
802                         return True;
803         } 
804
805         /* only left with a domain controller */
806
807         if ( strequal(get_global_sam_name(), domain) )  {
808                 return True;
809         }
810         
811         return False;
812 }
813
814 /* Parse a string of the form DOMAIN\user into a domain and a user */
815
816 BOOL parse_domain_user(const char *domuser, fstring domain, fstring user)
817 {
818         char *p = strchr(domuser,*lp_winbind_separator());
819
820         if ( !p ) {
821                 fstrcpy(user, domuser);
822
823                 if ( assume_domain(lp_workgroup())) {
824                         fstrcpy(domain, lp_workgroup());
825                 } else {
826                         return False;
827                 }
828         } else {
829                 fstrcpy(user, p+1);
830                 fstrcpy(domain, domuser);
831                 domain[PTR_DIFF(p, domuser)] = 0;
832         }
833         
834         strupper_m(domain);
835         
836         return True;
837 }
838
839 BOOL parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
840                               char **domain, char **user)
841 {
842         fstring fstr_domain, fstr_user;
843         if (!parse_domain_user(domuser, fstr_domain, fstr_user)) {
844                 return False;
845         }
846         *domain = talloc_strdup(mem_ctx, fstr_domain);
847         *user = talloc_strdup(mem_ctx, fstr_user);
848         return ((*domain != NULL) && (*user != NULL));
849 }
850
851 /* Ensure an incoming username from NSS is fully qualified. Replace the
852    incoming fstring with DOMAIN <separator> user. Returns the same
853    values as parse_domain_user() but also replaces the incoming username.
854    Used to ensure all names are fully qualified within winbindd.
855    Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
856    The protocol definitions of auth_crap, chng_pswd_auth_crap
857    really should be changed to use this instead of doing things
858    by hand. JRA. */
859
860 BOOL canonicalize_username(fstring username_inout, fstring domain, fstring user)
861 {
862         if (!parse_domain_user(username_inout, domain, user)) {
863                 return False;
864         }
865         slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
866                  domain, *lp_winbind_separator(),
867                  user);
868         return True;
869 }
870
871 /*
872     Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
873     'winbind separator' options.
874     This means:
875         - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
876         lp_workgroup()
877
878     If we are a PDC or BDC, and this is for our domain, do likewise.
879
880     Also, if omit DOMAIN if 'winbind trusted domains only = true', as the 
881     username is then unqualified in unix
882
883     We always canonicalize as UPPERCASE DOMAIN, lowercase username.
884 */
885 void fill_domain_username(fstring name, const char *domain, const char *user, BOOL can_assume)
886 {
887         fstring tmp_user;
888
889         fstrcpy(tmp_user, user);
890         strlower_m(tmp_user);
891
892         if (can_assume && assume_domain(domain)) {
893                 strlcpy(name, tmp_user, sizeof(fstring));
894         } else {
895                 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
896                          domain, *lp_winbind_separator(),
897                          tmp_user);
898         }
899 }
900
901 /*
902  * Winbindd socket accessor functions
903  */
904
905 char *get_winbind_priv_pipe_dir(void) 
906 {
907         return lock_path(WINBINDD_PRIV_SOCKET_SUBDIR);
908 }
909
910 /* Open the winbindd socket */
911
912 static int _winbindd_socket = -1;
913 static int _winbindd_priv_socket = -1;
914
915 int open_winbindd_socket(void)
916 {
917         if (_winbindd_socket == -1) {
918                 _winbindd_socket = create_pipe_sock(
919                         WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME, 0755);
920                 DEBUG(10, ("open_winbindd_socket: opened socket fd %d\n",
921                            _winbindd_socket));
922         }
923
924         return _winbindd_socket;
925 }
926
927 int open_winbindd_priv_socket(void)
928 {
929         if (_winbindd_priv_socket == -1) {
930                 _winbindd_priv_socket = create_pipe_sock(
931                         get_winbind_priv_pipe_dir(), WINBINDD_SOCKET_NAME, 0750);
932                 DEBUG(10, ("open_winbindd_priv_socket: opened socket fd %d\n",
933                            _winbindd_priv_socket));
934         }
935
936         return _winbindd_priv_socket;
937 }
938
939 /* Close the winbindd socket */
940
941 void close_winbindd_socket(void)
942 {
943         if (_winbindd_socket != -1) {
944                 DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
945                            _winbindd_socket));
946                 close(_winbindd_socket);
947                 _winbindd_socket = -1;
948         }
949         if (_winbindd_priv_socket != -1) {
950                 DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
951                            _winbindd_priv_socket));
952                 close(_winbindd_priv_socket);
953                 _winbindd_priv_socket = -1;
954         }
955 }
956
957 /*
958  * Client list accessor functions
959  */
960
961 static struct winbindd_cli_state *_client_list;
962 static int _num_clients;
963
964 /* Return list of all connected clients */
965
966 struct winbindd_cli_state *winbindd_client_list(void)
967 {
968         return _client_list;
969 }
970
971 /* Add a connection to the list */
972
973 void winbindd_add_client(struct winbindd_cli_state *cli)
974 {
975         DLIST_ADD(_client_list, cli);
976         _num_clients++;
977 }
978
979 /* Remove a client from the list */
980
981 void winbindd_remove_client(struct winbindd_cli_state *cli)
982 {
983         DLIST_REMOVE(_client_list, cli);
984         _num_clients--;
985 }
986
987 /* Close all open clients */
988
989 void winbindd_kill_all_clients(void)
990 {
991         struct winbindd_cli_state *cl = winbindd_client_list();
992
993         DEBUG(10, ("winbindd_kill_all_clients: going postal\n"));
994
995         while (cl) {
996                 struct winbindd_cli_state *next;
997                 
998                 next = cl->next;
999                 winbindd_remove_client(cl);
1000                 cl = next;
1001         }
1002 }
1003
1004 /* Return number of open clients */
1005
1006 int winbindd_num_clients(void)
1007 {
1008         return _num_clients;
1009 }
1010
1011 NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain,
1012                                   TALLOC_CTX *mem_ctx,
1013                                   const DOM_SID *user_sid,
1014                                   uint32 *p_num_groups, DOM_SID **user_sids)
1015 {
1016         NET_USER_INFO_3 *info3 = NULL;
1017         NTSTATUS status = NT_STATUS_NO_MEMORY;
1018         int i;
1019         size_t num_groups = 0;
1020         DOM_SID group_sid, primary_group;
1021         
1022         DEBUG(3,(": lookup_usergroups_cached\n"));
1023         
1024         *user_sids = NULL;
1025         num_groups = 0;
1026         *p_num_groups = 0;
1027
1028         info3 = netsamlogon_cache_get(mem_ctx, user_sid);
1029
1030         if (info3 == NULL) {
1031                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1032         }
1033
1034         if (info3->num_groups == 0) {
1035                 TALLOC_FREE(info3);
1036                 return NT_STATUS_UNSUCCESSFUL;
1037         }
1038         
1039         /* always add the primary group to the sid array */
1040         sid_compose(&primary_group, &info3->dom_sid.sid, info3->user_rid);
1041         
1042         if (!add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups)) {
1043                 TALLOC_FREE(info3);
1044                 return NT_STATUS_NO_MEMORY;
1045         }
1046
1047         for (i=0; i<info3->num_groups; i++) {
1048                 sid_copy(&group_sid, &info3->dom_sid.sid);
1049                 sid_append_rid(&group_sid, info3->gids[i].g_rid);
1050
1051                 if (!add_sid_to_array(mem_ctx, &group_sid, user_sids,
1052                                  &num_groups)) {
1053                         TALLOC_FREE(info3);
1054                         return NT_STATUS_NO_MEMORY;
1055                 }
1056         }
1057
1058         TALLOC_FREE(info3);
1059         *p_num_groups = num_groups;
1060         status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1061         
1062         DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
1063
1064         return status;
1065 }
1066
1067 /*********************************************************************
1068  We use this to remove spaces from user and group names
1069 ********************************************************************/
1070
1071 void ws_name_replace( char *name, char replace )
1072 {
1073         char replace_char[2] = { 0x0, 0x0 };
1074     
1075         if ( !lp_winbind_normalize_names() || (replace == '\0') ) 
1076                 return;
1077
1078         replace_char[0] = replace;      
1079         all_string_sub( name, " ", replace_char, 0 );
1080
1081         return; 
1082 }
1083
1084 /*********************************************************************
1085  We use this to do the inverse of ws_name_replace()
1086 ********************************************************************/
1087
1088 void ws_name_return( char *name, char replace )
1089 {
1090         char replace_char[2] = { 0x0, 0x0 };
1091     
1092         if ( !lp_winbind_normalize_names() || (replace == '\0') ) 
1093                 return;
1094         
1095         replace_char[0] = replace;      
1096         all_string_sub( name, replace_char, " ", 0 );
1097
1098         return; 
1099 }