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