r19413: Now we're calling init_dc_connection, this code
[abartlet/samba.git/.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 /**
31  * @file winbindd_util.c
32  *
33  * Winbind daemon for NT domain authentication nss module.
34  **/
35
36
37 /**
38  * Used to clobber name fields that have an undefined value.
39  *
40  * Correct code should never look at a field that has this value.
41  **/
42
43 static const fstring name_deadbeef = "<deadbeef>";
44
45 /* The list of trusted domains.  Note that the list can be deleted and
46    recreated using the init_domain_list() function so pointers to
47    individual winbindd_domain structures cannot be made.  Keep a copy of
48    the domain name instead. */
49
50 static struct winbindd_domain *_domain_list;
51
52 /**
53    When was the last scan of trusted domains done?
54    
55    0 == not ever
56 */
57
58 static time_t last_trustdom_scan;
59
60 struct winbindd_domain *domain_list(void)
61 {
62         /* Initialise list */
63
64         if ((!_domain_list) && (!init_domain_list())) {
65                 smb_panic("Init_domain_list failed\n");
66         }
67
68         return _domain_list;
69 }
70
71 /* Free all entries in the trusted domain list */
72
73 void free_domain_list(void)
74 {
75         struct winbindd_domain *domain = _domain_list;
76
77         while(domain) {
78                 struct winbindd_domain *next = domain->next;
79                 
80                 DLIST_REMOVE(_domain_list, domain);
81                 SAFE_FREE(domain);
82                 domain = next;
83         }
84 }
85
86 static BOOL is_internal_domain(const DOM_SID *sid)
87 {
88         if (sid == NULL)
89                 return False;
90
91         return (sid_check_is_domain(sid) || sid_check_is_builtin(sid));
92 }
93
94 static BOOL is_in_internal_domain(const DOM_SID *sid)
95 {
96         if (sid == NULL)
97                 return False;
98
99         return (sid_check_is_in_our_domain(sid) || sid_check_is_in_builtin(sid));
100 }
101
102
103 /* Add a trusted domain to our list of domains */
104 static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name,
105                                                   struct winbindd_methods *methods,
106                                                   const DOM_SID *sid)
107 {
108         struct winbindd_domain *domain;
109         const char *alternative_name = NULL;
110         
111         /* ignore alt_name if we are not in an AD domain */
112         
113         if ( (lp_security() == SEC_ADS) && alt_name && *alt_name) {
114                 alternative_name = alt_name;
115         }
116         
117         /* We can't call domain_list() as this function is called from
118            init_domain_list() and we'll get stuck in a loop. */
119         for (domain = _domain_list; domain; domain = domain->next) {
120                 if (strequal(domain_name, domain->name) ||
121                     strequal(domain_name, domain->alt_name)) {
122                         return domain;
123                 }
124                 if (alternative_name && *alternative_name) {
125                         if (strequal(alternative_name, domain->name) ||
126                             strequal(alternative_name, domain->alt_name)) {
127                                 return domain;
128                         }
129                 }
130                 if (sid) {
131                         if (is_null_sid(sid)) {
132                                 
133                         } else if (sid_equal(sid, &domain->sid)) {
134                                 return domain;
135                         }
136                 }
137         }
138         
139         /* Create new domain entry */
140
141         if ((domain = SMB_MALLOC_P(struct winbindd_domain)) == NULL)
142                 return NULL;
143
144         /* Fill in fields */
145         
146         ZERO_STRUCTP(domain);
147
148         /* prioritise the short name */
149         if (strchr_m(domain_name, '.') && alternative_name && *alternative_name) {
150                 fstrcpy(domain->name, alternative_name);
151                 fstrcpy(domain->alt_name, domain_name);
152         } else {
153                 fstrcpy(domain->name, domain_name);
154                 if (alternative_name) {
155                         fstrcpy(domain->alt_name, alternative_name);
156                 }
157         }
158
159         domain->methods = methods;
160         domain->backend = NULL;
161         domain->internal = is_internal_domain(sid);
162         domain->sequence_number = DOM_SEQUENCE_NONE;
163         domain->last_seq_check = 0;
164         domain->initialized = False;
165         domain->online = is_internal_domain(sid);
166         if (sid) {
167                 sid_copy(&domain->sid, sid);
168         }
169         
170         /* Link to domain list */
171         DLIST_ADD(_domain_list, domain);
172         
173         DEBUG(2,("Added domain %s %s %s\n", 
174                  domain->name, domain->alt_name,
175                  &domain->sid?sid_string_static(&domain->sid):""));
176         
177         return domain;
178 }
179
180 /********************************************************************
181   rescan our domains looking for new trusted domains
182 ********************************************************************/
183
184 struct trustdom_state {
185         TALLOC_CTX *mem_ctx;
186         struct winbindd_response *response;
187 };
188
189 static void trustdom_recv(void *private_data, BOOL success);
190
191 static void add_trusted_domains( struct winbindd_domain *domain )
192 {
193         TALLOC_CTX *mem_ctx;
194         struct winbindd_request *request;
195         struct winbindd_response *response;
196
197         struct trustdom_state *state;
198
199         mem_ctx = talloc_init("add_trusted_domains");
200         if (mem_ctx == NULL) {
201                 DEBUG(0, ("talloc_init failed\n"));
202                 return;
203         }
204
205         request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request);
206         response = TALLOC_P(mem_ctx, struct winbindd_response);
207         state = TALLOC_P(mem_ctx, struct trustdom_state);
208
209         if ((request == NULL) || (response == NULL) || (state == NULL)) {
210                 DEBUG(0, ("talloc failed\n"));
211                 talloc_destroy(mem_ctx);
212                 return;
213         }
214
215         state->mem_ctx = mem_ctx;
216         state->response = response;
217
218         request->length = sizeof(*request);
219         request->cmd = WINBINDD_LIST_TRUSTDOM;
220
221         async_domain_request(mem_ctx, domain, request, response,
222                              trustdom_recv, state);
223 }
224
225 static void trustdom_recv(void *private_data, BOOL success)
226 {
227         extern struct winbindd_methods cache_methods;
228         struct trustdom_state *state =
229                 talloc_get_type_abort(private_data, struct trustdom_state);
230         struct winbindd_response *response = state->response;
231         char *p;
232
233         if ((!success) || (response->result != WINBINDD_OK)) {
234                 DEBUG(1, ("Could not receive trustdoms\n"));
235                 talloc_destroy(state->mem_ctx);
236                 return;
237         }
238
239         p = (char *)response->extra_data.data;
240
241         while ((p != NULL) && (*p != '\0')) {
242                 char *q, *sidstr, *alt_name;
243                 DOM_SID sid;
244
245                 alt_name = strchr(p, '\\');
246                 if (alt_name == NULL) {
247                         DEBUG(0, ("Got invalid trustdom response\n"));
248                         break;
249                 }
250
251                 *alt_name = '\0';
252                 alt_name += 1;
253
254                 sidstr = strchr(alt_name, '\\');
255                 if (sidstr == NULL) {
256                         DEBUG(0, ("Got invalid trustdom response\n"));
257                         break;
258                 }
259
260                 *sidstr = '\0';
261                 sidstr += 1;
262
263                 q = strchr(sidstr, '\n');
264                 if (q != NULL)
265                         *q = '\0';
266
267                 if (!string_to_sid(&sid, sidstr)) {
268                         DEBUG(0, ("Got invalid trustdom response\n"));
269                         break;
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         /* save online flag */
385         request_domain = find_our_domain();
386         request_domain->online = domain->online;
387         
388         async_domain_request(mem_ctx, request_domain, request, response,
389                              init_child_getdc_recv, state);
390         return WINBINDD_PENDING;
391 }
392
393 static void init_child_getdc_recv(void *private_data, BOOL success)
394 {
395         struct init_child_state *state =
396                 talloc_get_type_abort(private_data, struct init_child_state);
397         const char *dcname = "";
398
399         DEBUG(10, ("Received getdcname response\n"));
400
401         if (success && (state->response->result == WINBINDD_OK)) {
402                 dcname = state->response->data.dc_name;
403         }
404
405         state->request->cmd = WINBINDD_INIT_CONNECTION;
406         fstrcpy(state->request->domain_name, state->domain->name);
407         state->request->data.init_conn.is_primary = False;
408         fstrcpy(state->request->data.init_conn.dcname, dcname);
409
410         async_request(state->mem_ctx, &state->domain->child,
411                       state->request, state->response,
412                       init_child_recv, state);
413 }
414
415 static void init_child_recv(void *private_data, BOOL success)
416 {
417         struct init_child_state *state =
418                 talloc_get_type_abort(private_data, struct init_child_state);
419
420         DEBUG(5, ("Received child initialization response for domain %s\n",
421                   state->domain->name));
422
423         if ((!success) || (state->response->result != WINBINDD_OK)) {
424                 DEBUG(3, ("Could not init child\n"));
425                 state->continuation(state->private_data, False);
426                 talloc_destroy(state->mem_ctx);
427                 return;
428         }
429
430         fstrcpy(state->domain->name,
431                 state->response->data.domain_info.name);
432         fstrcpy(state->domain->alt_name,
433                 state->response->data.domain_info.alt_name);
434         string_to_sid(&state->domain->sid,
435                       state->response->data.domain_info.sid);
436         state->domain->native_mode =
437                 state->response->data.domain_info.native_mode;
438         state->domain->active_directory =
439                 state->response->data.domain_info.active_directory;
440         state->domain->sequence_number =
441                 state->response->data.domain_info.sequence_number;
442
443         state->domain->initialized = 1;
444
445         if (state->continuation != NULL)
446                 state->continuation(state->private_data, True);
447         talloc_destroy(state->mem_ctx);
448 }
449
450 enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
451                                                    struct winbindd_cli_state *state)
452 {
453         /* Ensure null termination */
454         state->request.domain_name
455                 [sizeof(state->request.domain_name)-1]='\0';
456         state->request.data.init_conn.dcname
457                 [sizeof(state->request.data.init_conn.dcname)-1]='\0';
458
459         if (strlen(state->request.data.init_conn.dcname) > 0) {
460                 fstrcpy(domain->dcname, state->request.data.init_conn.dcname);
461         }
462
463         init_dc_connection(domain);
464
465 #if 1
466         if (!domain->initialized) {
467                 /* If we return error here we can't do any cached authentication,
468                    but we may be in disconnected mode and can't initialize correctly.
469                    Do what the previous code did and just return without initialization,
470                    once we go online we'll re-initialize.
471                 */
472                 DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization "
473                         "online = %d\n", domain->name, (int)domain->online ));
474         }
475 #else
476         if (!domain->initialized) {
477                 DEBUG(1, ("Could not initialize domain %s\n",
478                           state->request.domain_name));
479                 return WINBINDD_ERROR;
480         }
481 #endif
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         extern struct winbindd_methods cache_methods;
504         extern struct winbindd_methods passdb_methods;
505         struct winbindd_domain *domain;
506         int role = lp_server_role();
507
508         /* Free existing list */
509         free_domain_list();
510
511         /* Add ourselves as the first entry. */
512
513         if ( role == ROLE_DOMAIN_MEMBER ) {
514                 DOM_SID our_sid;
515
516                 if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
517                         DEBUG(0, ("Could not fetch our SID - did we join?\n"));
518                         return False;
519                 }
520         
521                 domain = add_trusted_domain( lp_workgroup(), lp_realm(),
522                                              &cache_methods, &our_sid);
523                 domain->primary = True;
524                 setup_domain_child(domain, &domain->child, NULL);
525         }
526
527         /* Local SAM */
528
529         domain = add_trusted_domain(get_global_sam_name(), NULL,
530                                     &passdb_methods, get_global_sam_sid());
531         if ( role != ROLE_DOMAIN_MEMBER ) {
532                 domain->primary = True;
533         }
534         setup_domain_child(domain, &domain->child, NULL);
535
536         /* BUILTIN domain */
537
538         domain = add_trusted_domain("BUILTIN", NULL, &passdb_methods,
539                                     &global_sid_Builtin);
540         setup_domain_child(domain, &domain->child, NULL);
541
542         return True;
543 }
544
545 /** 
546  * Given a domain name, return the struct winbindd domain info for it 
547  *
548  * @note Do *not* pass lp_workgroup() to this function.  domain_list
549  *       may modify it's value, and free that pointer.  Instead, our local
550  *       domain may be found by calling find_our_domain().
551  *       directly.
552  *
553  *
554  * @return The domain structure for the named domain, if it is working.
555  */
556
557 struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name)
558 {
559         struct winbindd_domain *domain;
560
561         /* Search through list */
562
563         for (domain = domain_list(); domain != NULL; domain = domain->next) {
564                 if (strequal(domain_name, domain->name) ||
565                     (domain->alt_name[0] &&
566                      strequal(domain_name, domain->alt_name))) {
567                         return domain;
568                 }
569         }
570
571         /* Not found */
572
573         return NULL;
574 }
575
576 struct winbindd_domain *find_domain_from_name(const char *domain_name)
577 {
578         struct winbindd_domain *domain;
579
580         domain = find_domain_from_name_noinit(domain_name);
581
582         if (domain == NULL)
583                 return NULL;
584
585         if (!domain->initialized)
586                 init_dc_connection(domain);
587
588         return domain;
589 }
590
591 /* Given a domain sid, return the struct winbindd domain info for it */
592
593 struct winbindd_domain *find_domain_from_sid_noinit(const DOM_SID *sid)
594 {
595         struct winbindd_domain *domain;
596
597         /* Search through list */
598
599         for (domain = domain_list(); domain != NULL; domain = domain->next) {
600                 if (sid_compare_domain(sid, &domain->sid) == 0)
601                         return domain;
602         }
603
604         /* Not found */
605
606         return NULL;
607 }
608
609 /* Given a domain sid, return the struct winbindd domain info for it */
610
611 struct winbindd_domain *find_domain_from_sid(const DOM_SID *sid)
612 {
613         struct winbindd_domain *domain;
614
615         domain = find_domain_from_sid_noinit(sid);
616
617         if (domain == NULL)
618                 return NULL;
619
620         if (!domain->initialized)
621                 init_dc_connection(domain);
622
623         return domain;
624 }
625
626 struct winbindd_domain *find_our_domain(void)
627 {
628         struct winbindd_domain *domain;
629
630         /* Search through list */
631
632         for (domain = domain_list(); domain != NULL; domain = domain->next) {
633                 if (domain->primary)
634                         return domain;
635         }
636
637         smb_panic("Could not find our domain\n");
638         return NULL;
639 }
640
641 struct winbindd_domain *find_builtin_domain(void)
642 {
643         DOM_SID sid;
644         struct winbindd_domain *domain;
645
646         string_to_sid(&sid, "S-1-5-32");
647         domain = find_domain_from_sid(&sid);
648
649         if (domain == NULL)
650                 smb_panic("Could not find BUILTIN domain\n");
651
652         return domain;
653 }
654
655 /* Find the appropriate domain to lookup a name or SID */
656
657 struct winbindd_domain *find_lookup_domain_from_sid(const DOM_SID *sid)
658 {
659         /* A DC can't ask the local smbd for remote SIDs, here winbindd is the
660          * one to contact the external DC's. On member servers the internal
661          * domains are different: These are part of the local SAM. */
662
663         DEBUG(10, ("find_lookup_domain_from_sid(%s)\n",
664                    sid_string_static(sid)));
665
666         if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) {
667                 DEBUG(10, ("calling find_domain_from_sid\n"));
668                 return find_domain_from_sid(sid);
669         }
670
671         /* On a member server a query for SID or name can always go to our
672          * primary DC. */
673
674         DEBUG(10, ("calling find_our_domain\n"));
675         return find_our_domain();
676 }
677
678 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
679 {
680         if (IS_DC || strequal(domain_name, "BUILTIN") ||
681             strequal(domain_name, get_global_sam_name()))
682                 return find_domain_from_name_noinit(domain_name);
683
684         return find_our_domain();
685 }
686
687 /* Lookup a sid in a domain from a name */
688
689 BOOL winbindd_lookup_sid_by_name(TALLOC_CTX *mem_ctx,
690                                  struct winbindd_domain *domain, 
691                                  const char *domain_name,
692                                  const char *name, DOM_SID *sid, 
693                                  enum lsa_SidType *type)
694 {
695         NTSTATUS result;
696
697         /* Lookup name */
698         result = domain->methods->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
699
700         /* Return rid and type if lookup successful */
701         if (!NT_STATUS_IS_OK(result)) {
702                 *type = SID_NAME_UNKNOWN;
703         }
704
705         return NT_STATUS_IS_OK(result);
706 }
707
708 /**
709  * @brief Lookup a name in a domain from a sid.
710  *
711  * @param sid Security ID you want to look up.
712  * @param name On success, set to the name corresponding to @p sid.
713  * @param dom_name On success, set to the 'domain name' corresponding to @p sid.
714  * @param type On success, contains the type of name: alias, group or
715  * user.
716  * @retval True if the name exists, in which case @p name and @p type
717  * are set, otherwise False.
718  **/
719 BOOL winbindd_lookup_name_by_sid(TALLOC_CTX *mem_ctx,
720                                  DOM_SID *sid,
721                                  fstring dom_name,
722                                  fstring name,
723                                  enum lsa_SidType *type)
724 {
725         char *names;
726         char *dom_names;
727         NTSTATUS result;
728         BOOL rv = False;
729         struct winbindd_domain *domain;
730
731         domain = find_lookup_domain_from_sid(sid);
732
733         if (!domain) {
734                 DEBUG(1,("Can't find domain from sid\n"));
735                 return False;
736         }
737
738         /* Lookup name */
739
740         result = domain->methods->sid_to_name(domain, mem_ctx, sid, &dom_names, &names, type);
741
742         /* Return name and type if successful */
743         
744         if ((rv = NT_STATUS_IS_OK(result))) {
745                 fstrcpy(dom_name, dom_names);
746                 fstrcpy(name, names);
747         } else {
748                 *type = SID_NAME_UNKNOWN;
749                 fstrcpy(name, name_deadbeef);
750         }
751         
752         return rv;
753 }
754
755 /* Free state information held for {set,get,end}{pw,gr}ent() functions */
756
757 void free_getent_state(struct getent_state *state)
758 {
759         struct getent_state *temp;
760
761         /* Iterate over state list */
762
763         temp = state;
764
765         while(temp != NULL) {
766                 struct getent_state *next;
767
768                 /* Free sam entries then list entry */
769
770                 SAFE_FREE(state->sam_entries);
771                 DLIST_REMOVE(state, state);
772                 next = temp->next;
773
774                 SAFE_FREE(temp);
775                 temp = next;
776         }
777 }
778
779 /* Parse winbindd related parameters */
780
781 BOOL winbindd_param_init(void)
782 {
783         /* Parse winbind uid and winbind_gid parameters */
784
785         if (!lp_idmap_uid(&server_state.uid_low, &server_state.uid_high)) {
786                 DEBUG(0, ("winbindd: idmap uid range missing or invalid\n"));
787                 DEBUG(0, ("winbindd: cannot continue, exiting.\n"));
788                 return False;
789         }
790         
791         if (!lp_idmap_gid(&server_state.gid_low, &server_state.gid_high)) {
792                 DEBUG(0, ("winbindd: idmap gid range missing or invalid\n"));
793                 DEBUG(0, ("winbindd: cannot continue, exiting.\n"));
794                 return False;
795         }
796         
797         return True;
798 }
799
800 BOOL is_in_uid_range(uid_t uid)
801 {
802         return ((uid >= server_state.uid_low) &&
803                 (uid <= server_state.uid_high));
804 }
805
806 BOOL is_in_gid_range(gid_t gid)
807 {
808         return ((gid >= server_state.gid_low) &&
809                 (gid <= server_state.gid_high));
810 }
811
812 /* Is this a domain which we may assume no DOMAIN\ prefix? */
813
814 static BOOL assume_domain(const char *domain)
815 {
816         /* never assume the domain on a standalone server */
817
818         if ( lp_server_role() == ROLE_STANDALONE )
819                 return False;
820
821         /* domain member servers may possibly assume for the domain name */
822
823         if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
824                 if ( !strequal(lp_workgroup(), domain) )
825                         return False;
826
827                 if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() )
828                         return True;
829         } 
830
831         /* only left with a domain controller */
832
833         if ( strequal(get_global_sam_name(), domain) )  {
834                 return True;
835         }
836         
837         return False;
838 }
839
840 /* Parse a string of the form DOMAIN\user into a domain and a user */
841
842 BOOL parse_domain_user(const char *domuser, fstring domain, fstring user)
843 {
844         char *p = strchr(domuser,*lp_winbind_separator());
845
846         if ( !p ) {
847                 fstrcpy(user, domuser);
848
849                 if ( assume_domain(lp_workgroup())) {
850                         fstrcpy(domain, lp_workgroup());
851                 } else {
852                         return False;
853                 }
854         } else {
855                 fstrcpy(user, p+1);
856                 fstrcpy(domain, domuser);
857                 domain[PTR_DIFF(p, domuser)] = 0;
858         }
859         
860         strupper_m(domain);
861         
862         return True;
863 }
864
865 BOOL parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
866                               char **domain, char **user)
867 {
868         fstring fstr_domain, fstr_user;
869         if (!parse_domain_user(domuser, fstr_domain, fstr_user)) {
870                 return False;
871         }
872         *domain = talloc_strdup(mem_ctx, fstr_domain);
873         *user = talloc_strdup(mem_ctx, fstr_user);
874         return ((*domain != NULL) && (*user != NULL));
875 }
876
877 /* Ensure an incoming username from NSS is fully qualified. Replace the
878    incoming fstring with DOMAIN <separator> user. Returns the same
879    values as parse_domain_user() but also replaces the incoming username.
880    Used to ensure all names are fully qualified within winbindd.
881    Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
882    The protocol definitions of auth_crap, chng_pswd_auth_crap
883    really should be changed to use this instead of doing things
884    by hand. JRA. */
885
886 BOOL canonicalize_username(fstring username_inout, fstring domain, fstring user)
887 {
888         if (!parse_domain_user(username_inout, domain, user)) {
889                 return False;
890         }
891         slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
892                  domain, *lp_winbind_separator(),
893                  user);
894         return True;
895 }
896
897 /*
898     Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
899     'winbind separator' options.
900     This means:
901         - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
902         lp_workgroup()
903
904     If we are a PDC or BDC, and this is for our domain, do likewise.
905
906     Also, if omit DOMAIN if 'winbind trusted domains only = true', as the 
907     username is then unqualified in unix
908
909     We always canonicalize as UPPERCASE DOMAIN, lowercase username.
910 */
911 void fill_domain_username(fstring name, const char *domain, const char *user, BOOL can_assume)
912 {
913         fstring tmp_user;
914
915         fstrcpy(tmp_user, user);
916         strlower_m(tmp_user);
917
918         if (can_assume && assume_domain(domain)) {
919                 strlcpy(name, tmp_user, sizeof(fstring));
920         } else {
921                 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
922                          domain, *lp_winbind_separator(),
923                          tmp_user);
924         }
925 }
926
927 /*
928  * Winbindd socket accessor functions
929  */
930
931 char *get_winbind_priv_pipe_dir(void) 
932 {
933         return lock_path(WINBINDD_PRIV_SOCKET_SUBDIR);
934 }
935
936 /* Open the winbindd socket */
937
938 static int _winbindd_socket = -1;
939 static int _winbindd_priv_socket = -1;
940
941 int open_winbindd_socket(void)
942 {
943         if (_winbindd_socket == -1) {
944                 _winbindd_socket = create_pipe_sock(
945                         WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME, 0755);
946                 DEBUG(10, ("open_winbindd_socket: opened socket fd %d\n",
947                            _winbindd_socket));
948         }
949
950         return _winbindd_socket;
951 }
952
953 int open_winbindd_priv_socket(void)
954 {
955         if (_winbindd_priv_socket == -1) {
956                 _winbindd_priv_socket = create_pipe_sock(
957                         get_winbind_priv_pipe_dir(), WINBINDD_SOCKET_NAME, 0750);
958                 DEBUG(10, ("open_winbindd_priv_socket: opened socket fd %d\n",
959                            _winbindd_priv_socket));
960         }
961
962         return _winbindd_priv_socket;
963 }
964
965 /* Close the winbindd socket */
966
967 void close_winbindd_socket(void)
968 {
969         if (_winbindd_socket != -1) {
970                 DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
971                            _winbindd_socket));
972                 close(_winbindd_socket);
973                 _winbindd_socket = -1;
974         }
975         if (_winbindd_priv_socket != -1) {
976                 DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
977                            _winbindd_priv_socket));
978                 close(_winbindd_priv_socket);
979                 _winbindd_priv_socket = -1;
980         }
981 }
982
983 /*
984  * Client list accessor functions
985  */
986
987 static struct winbindd_cli_state *_client_list;
988 static int _num_clients;
989
990 /* Return list of all connected clients */
991
992 struct winbindd_cli_state *winbindd_client_list(void)
993 {
994         return _client_list;
995 }
996
997 /* Add a connection to the list */
998
999 void winbindd_add_client(struct winbindd_cli_state *cli)
1000 {
1001         DLIST_ADD(_client_list, cli);
1002         _num_clients++;
1003 }
1004
1005 /* Remove a client from the list */
1006
1007 void winbindd_remove_client(struct winbindd_cli_state *cli)
1008 {
1009         DLIST_REMOVE(_client_list, cli);
1010         _num_clients--;
1011 }
1012
1013 /* Close all open clients */
1014
1015 void winbindd_kill_all_clients(void)
1016 {
1017         struct winbindd_cli_state *cl = winbindd_client_list();
1018
1019         DEBUG(10, ("winbindd_kill_all_clients: going postal\n"));
1020
1021         while (cl) {
1022                 struct winbindd_cli_state *next;
1023                 
1024                 next = cl->next;
1025                 winbindd_remove_client(cl);
1026                 cl = next;
1027         }
1028 }
1029
1030 /* Return number of open clients */
1031
1032 int winbindd_num_clients(void)
1033 {
1034         return _num_clients;
1035 }
1036
1037 /*****************************************************************************
1038  For idmap conversion: convert one record to new format
1039  Ancient versions (eg 2.2.3a) of winbindd_idmap.tdb mapped DOMAINNAME/rid
1040  instead of the SID.
1041 *****************************************************************************/
1042 static int convert_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA data, void *state)
1043 {
1044         struct winbindd_domain *domain;
1045         char *p;
1046         DOM_SID sid;
1047         uint32 rid;
1048         fstring keystr;
1049         fstring dom_name;
1050         TDB_DATA key2;
1051         BOOL *failed = (BOOL *)state;
1052
1053         DEBUG(10,("Converting %s\n", key.dptr));
1054
1055         p = strchr(key.dptr, '/');
1056         if (!p)
1057                 return 0;
1058
1059         *p = 0;
1060         fstrcpy(dom_name, key.dptr);
1061         *p++ = '/';
1062
1063         domain = find_domain_from_name(dom_name);
1064         if (domain == NULL) {
1065                 /* We must delete the old record. */
1066                 DEBUG(0,("Unable to find domain %s\n", dom_name ));
1067                 DEBUG(0,("deleting record %s\n", key.dptr ));
1068
1069                 if (tdb_delete(tdb, key) != 0) {
1070                         DEBUG(0, ("Unable to delete record %s\n", key.dptr));
1071                         *failed = True;
1072                         return -1;
1073                 }
1074
1075                 return 0;
1076         }
1077
1078         rid = atoi(p);
1079
1080         sid_copy(&sid, &domain->sid);
1081         sid_append_rid(&sid, rid);
1082
1083         sid_to_string(keystr, &sid);
1084         key2.dptr = keystr;
1085         key2.dsize = strlen(keystr) + 1;
1086
1087         if (tdb_store(tdb, key2, data, TDB_INSERT) != 0) {
1088                 DEBUG(0,("Unable to add record %s\n", key2.dptr ));
1089                 *failed = True;
1090                 return -1;
1091         }
1092
1093         if (tdb_store(tdb, data, key2, TDB_REPLACE) != 0) {
1094                 DEBUG(0,("Unable to update record %s\n", data.dptr ));
1095                 *failed = True;
1096                 return -1;
1097         }
1098
1099         if (tdb_delete(tdb, key) != 0) {
1100                 DEBUG(0,("Unable to delete record %s\n", key.dptr ));
1101                 *failed = True;
1102                 return -1;
1103         }
1104
1105         return 0;
1106 }
1107
1108 /* These definitions are from sam/idmap_tdb.c. Replicated here just
1109    out of laziness.... :-( */
1110
1111 /* High water mark keys */
1112 #define HWM_GROUP  "GROUP HWM"
1113 #define HWM_USER   "USER HWM"
1114
1115 /*****************************************************************************
1116  Convert the idmap database from an older version.
1117 *****************************************************************************/
1118
1119 static BOOL idmap_convert(const char *idmap_name)
1120 {
1121         int32 vers;
1122         BOOL bigendianheader;
1123         BOOL failed = False;
1124         TDB_CONTEXT *idmap_tdb;
1125
1126         if (!(idmap_tdb = tdb_open_log(idmap_name, 0,
1127                                         TDB_DEFAULT, O_RDWR,
1128                                         0600))) {
1129                 DEBUG(0, ("idmap_convert: Unable to open idmap database\n"));
1130                 return False;
1131         }
1132
1133         bigendianheader = (tdb_get_flags(idmap_tdb) & TDB_BIGENDIAN) ? True : False;
1134
1135         vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
1136
1137         if (((vers == -1) && bigendianheader) || (IREV(vers) == IDMAP_VERSION)) {
1138                 /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */
1139                 /*
1140                  * high and low records were created on a
1141                  * big endian machine and will need byte-reversing.
1142                  */
1143
1144                 int32 wm;
1145
1146                 wm = tdb_fetch_int32(idmap_tdb, HWM_USER);
1147
1148                 if (wm != -1) {
1149                         wm = IREV(wm);
1150                 }  else {
1151                         wm = server_state.uid_low;
1152                 }
1153
1154                 if (tdb_store_int32(idmap_tdb, HWM_USER, wm) == -1) {
1155                         DEBUG(0, ("idmap_convert: Unable to byteswap user hwm in idmap database\n"));
1156                         tdb_close(idmap_tdb);
1157                         return False;
1158                 }
1159
1160                 wm = tdb_fetch_int32(idmap_tdb, HWM_GROUP);
1161                 if (wm != -1) {
1162                         wm = IREV(wm);
1163                 } else {
1164                         wm = server_state.gid_low;
1165                 }
1166
1167                 if (tdb_store_int32(idmap_tdb, HWM_GROUP, wm) == -1) {
1168                         DEBUG(0, ("idmap_convert: Unable to byteswap group hwm in idmap database\n"));
1169                         tdb_close(idmap_tdb);
1170                         return False;
1171                 }
1172         }
1173
1174         /* the old format stored as DOMAIN/rid - now we store the SID direct */
1175         tdb_traverse(idmap_tdb, convert_fn, &failed);
1176
1177         if (failed) {
1178                 DEBUG(0, ("Problem during conversion\n"));
1179                 tdb_close(idmap_tdb);
1180                 return False;
1181         }
1182
1183         if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) == -1) {
1184                 DEBUG(0, ("idmap_convert: Unable to dtore idmap version in databse\n"));
1185                 tdb_close(idmap_tdb);
1186                 return False;
1187         }
1188
1189         tdb_close(idmap_tdb);
1190         return True;
1191 }
1192
1193 /*****************************************************************************
1194  Convert the idmap database from an older version if necessary
1195 *****************************************************************************/
1196
1197 BOOL winbindd_upgrade_idmap(void)
1198 {
1199         pstring idmap_name;
1200         pstring backup_name;
1201         SMB_STRUCT_STAT stbuf;
1202         TDB_CONTEXT *idmap_tdb;
1203
1204         pstrcpy(idmap_name, lock_path("winbindd_idmap.tdb"));
1205
1206         if (!file_exist(idmap_name, &stbuf)) {
1207                 /* nothing to convert return */
1208                 return True;
1209         }
1210
1211         if (!(idmap_tdb = tdb_open_log(idmap_name, 0,
1212                                         TDB_DEFAULT, O_RDWR,
1213                                         0600))) {
1214                 DEBUG(0, ("idmap_convert: Unable to open idmap database\n"));
1215                 return False;
1216         }
1217
1218         if (tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION") == IDMAP_VERSION) {
1219                 /* nothing to convert return */
1220                 tdb_close(idmap_tdb);
1221                 return True;
1222         }
1223
1224         /* backup_tdb expects the tdb not to be open */
1225         tdb_close(idmap_tdb);
1226
1227         DEBUG(0, ("Upgrading winbindd_idmap.tdb from an old version\n"));
1228
1229         pstrcpy(backup_name, idmap_name);
1230         pstrcat(backup_name, ".bak");
1231
1232         if (backup_tdb(idmap_name, backup_name) != 0) {
1233                 DEBUG(0, ("Could not backup idmap database\n"));
1234                 return False;
1235         }
1236
1237         return idmap_convert(idmap_name);
1238 }
1239
1240 NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain,
1241                                   TALLOC_CTX *mem_ctx,
1242                                   const DOM_SID *user_sid,
1243                                   uint32 *p_num_groups, DOM_SID **user_sids)
1244 {
1245         NET_USER_INFO_3 *info3 = NULL;
1246         NTSTATUS status = NT_STATUS_NO_MEMORY;
1247         int i;
1248         size_t num_groups = 0;
1249         DOM_SID group_sid, primary_group;
1250         
1251         DEBUG(3,(": lookup_usergroups_cached\n"));
1252         
1253         *user_sids = NULL;
1254         num_groups = 0;
1255         *p_num_groups = 0;
1256
1257         info3 = netsamlogon_cache_get(mem_ctx, user_sid);
1258
1259         if (info3 == NULL) {
1260                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1261         }
1262
1263         if (info3->num_groups == 0) {
1264                 SAFE_FREE(info3);
1265                 return NT_STATUS_UNSUCCESSFUL;
1266         }
1267         
1268         /* always add the primary group to the sid array */
1269         sid_compose(&primary_group, &info3->dom_sid.sid, info3->user_rid);
1270         
1271         add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups);
1272
1273         for (i=0; i<info3->num_groups; i++) {
1274                 sid_copy(&group_sid, &info3->dom_sid.sid);
1275                 sid_append_rid(&group_sid, info3->gids[i].g_rid);
1276
1277                 add_sid_to_array(mem_ctx, &group_sid, user_sids,
1278                                  &num_groups);
1279         }
1280
1281         SAFE_FREE(info3);
1282         *p_num_groups = num_groups;
1283         status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1284         
1285         DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
1286
1287         return status;
1288 }