Replace sid_string_static by sid_string_dbg in DEBUGs
[amitay/samba.git] / source3 / winbindd / winbindd_user.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon - user related functions
5
6    Copyright (C) Tim Potter 2000
7    Copyright (C) Jeremy Allison 2001.
8    Copyright (C) Gerald (Jerry) Carter 2003.
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "winbindd.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
29
30 static bool fillup_pw_field(const char *lp_template, 
31                             const char *username, 
32                             const char *domname,
33                             uid_t uid,
34                             gid_t gid,
35                             const char *in, 
36                             fstring out)
37 {
38         char *templ;
39
40         if (out == NULL)
41                 return False;
42
43         /* The substitution of %U and %D in the 'template 
44            homedir' is done by talloc_sub_specified() below.
45            If we have an in string (which means the value has already
46            been set in the nss_info backend), then use that.
47            Otherwise use the template value passed in. */
48
49         if ( in && !strequal(in,"") && lp_security() == SEC_ADS ) {
50                 templ = talloc_sub_specified(NULL, in, 
51                                              username, domname,
52                                      uid, gid);
53         } else {
54                 templ = talloc_sub_specified(NULL, lp_template, 
55                                              username, domname,
56                                              uid, gid);         
57         }
58                 
59         if (!templ)
60                 return False;
61
62         safe_strcpy(out, templ, sizeof(fstring) - 1);
63         TALLOC_FREE(templ);
64                 
65         return True;
66         
67 }
68 /* Fill a pwent structure with information we have obtained */
69
70 static bool winbindd_fill_pwent(char *dom_name, char *user_name, 
71                                 DOM_SID *user_sid, DOM_SID *group_sid,
72                                 char *full_name, char *homedir, char *shell,
73                                 struct winbindd_pw *pw)
74 {
75         fstring output_username;
76         
77         if (!pw || !dom_name || !user_name)
78                 return False;
79         
80         /* Resolve the uid number */
81
82         if (!NT_STATUS_IS_OK(idmap_sid_to_uid(user_sid, &pw->pw_uid))) {
83                 DEBUG(1, ("error getting user id for sid %s\n",
84                           sid_string_dbg(user_sid)));
85                 return False;
86         }
87         
88         /* Resolve the gid number */   
89
90         if (!NT_STATUS_IS_OK(idmap_sid_to_gid(group_sid, &pw->pw_gid))) {
91                 DEBUG(1, ("error getting group id for sid %s\n",
92                           sid_string_dbg(group_sid)));
93                 return False;
94         }
95
96         strlower_m(user_name);
97
98         /* Username */
99
100         fill_domain_username(output_username, dom_name, user_name, True); 
101
102         safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
103         
104         /* Full name (gecos) */
105         
106         safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
107
108         /* Home directory and shell */
109         
110         if (!fillup_pw_field(lp_template_homedir(), user_name, dom_name, 
111                              pw->pw_uid, pw->pw_gid, homedir, pw->pw_dir))
112                 return False;
113
114         if (!fillup_pw_field(lp_template_shell(), user_name, dom_name, 
115                              pw->pw_uid, pw->pw_gid, shell, pw->pw_shell))
116                 return False;
117
118         /* Password - set to "*" as we can't generate anything useful here.
119            Authentication can be done using the pam_winbind module. */
120
121         safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
122
123         return True;
124 }
125
126 /* Wrapper for domain->methods->query_user, only on the parent->child pipe */
127
128 enum winbindd_result winbindd_dual_userinfo(struct winbindd_domain *domain,
129                                             struct winbindd_cli_state *state)
130 {
131         DOM_SID sid;
132         WINBIND_USERINFO user_info;
133         NTSTATUS status;
134
135         /* Ensure null termination */
136         state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
137
138         DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid, 
139                   state->request.data.sid));
140
141         if (!string_to_sid(&sid, state->request.data.sid)) {
142                 DEBUG(5, ("%s not a SID\n", state->request.data.sid));
143                 return WINBINDD_ERROR;
144         }
145
146         status = domain->methods->query_user(domain, state->mem_ctx,
147                                              &sid, &user_info);
148         if (!NT_STATUS_IS_OK(status)) {
149                 DEBUG(1, ("error getting user info for sid %s\n",
150                           sid_string_dbg(&sid)));
151                 return WINBINDD_ERROR;
152         }
153
154         fstrcpy(state->response.data.user_info.acct_name, user_info.acct_name);
155         fstrcpy(state->response.data.user_info.full_name, user_info.full_name);
156         fstrcpy(state->response.data.user_info.homedir, user_info.homedir);
157         fstrcpy(state->response.data.user_info.shell, user_info.shell);
158         state->response.data.user_info.primary_gid = user_info.primary_gid;     
159         if (!sid_peek_check_rid(&domain->sid, &user_info.group_sid,
160                                 &state->response.data.user_info.group_rid)) {
161                 DEBUG(1, ("Could not extract group rid out of %s\n",
162                           sid_string_dbg(&sid)));
163                 return WINBINDD_ERROR;
164         }
165
166         return WINBINDD_OK;
167 }
168
169 struct getpwsid_state {
170         struct winbindd_cli_state *state;
171         struct winbindd_domain *domain;
172         char *username;
173         char *fullname;
174         char *homedir;
175         char *shell;
176         DOM_SID user_sid;
177         uid_t uid;
178         DOM_SID group_sid;
179         gid_t gid;
180 };
181
182 static void getpwsid_queryuser_recv(void *private_data, bool success,
183                                     const char *acct_name,
184                                     const char *full_name, 
185                                     const char *homedir,
186                                     const char *shell,
187                                     uint32 gid,
188                                     uint32 group_rid);
189 static void getpwsid_sid2uid_recv(void *private_data, bool success, uid_t uid);
190 static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid);
191
192 static void winbindd_getpwsid(struct winbindd_cli_state *state,
193                               const DOM_SID *sid)
194 {
195         struct getpwsid_state *s;
196
197         s = TALLOC_ZERO_P(state->mem_ctx, struct getpwsid_state);
198         if (s == NULL) {
199                 DEBUG(0, ("talloc failed\n"));
200                 goto error;
201         }
202
203         s->state = state;
204         s->domain = find_domain_from_sid_noinit(sid);
205         if (s->domain == NULL) {
206                 DEBUG(3, ("Could not find domain for sid %s\n",
207                           sid_string_dbg(sid)));
208                 goto error;
209         }
210
211         sid_copy(&s->user_sid, sid);
212
213         query_user_async(s->state->mem_ctx, s->domain, sid,
214                          getpwsid_queryuser_recv, s);
215         return;
216
217  error:
218         request_error(state);
219 }
220         
221 static void getpwsid_queryuser_recv(void *private_data, bool success,
222                                     const char *acct_name,
223                                     const char *full_name, 
224                                     const char *homedir,
225                                     const char *shell,
226                                     uint32 gid,
227                                     uint32 group_rid)
228 {
229         fstring username;
230         struct getpwsid_state *s =
231                 talloc_get_type_abort(private_data, struct getpwsid_state);
232
233         if (!success) {
234                 DEBUG(5, ("Could not query domain %s SID %s\n",
235                           s->domain->name, sid_string_dbg(&s->user_sid)));
236                 request_error(s->state);
237                 return;
238         }
239
240         if ( acct_name && *acct_name ) {
241         fstrcpy( username, acct_name );
242         } else {                
243                 char *domain_name = NULL;
244                 enum lsa_SidType type;
245                 char *user_name = NULL;
246                 struct winbindd_domain *domain = NULL;
247                 
248                 domain = find_lookup_domain_from_sid(&s->user_sid);
249                 if (domain == NULL) {
250                         DEBUG(5, ("find_lookup_domain_from_sid(%s) failed\n",
251                                   sid_string_dbg(&s->user_sid)));
252                         request_error(s->state);
253                         return;                 
254                 }
255                 winbindd_lookup_name_by_sid(s->state->mem_ctx, domain,
256                                             &s->user_sid, &domain_name,
257                                             &user_name, &type );                
258
259                 /* If this still fails we ar4e done.  Just error out */
260                 if ( !user_name ) {
261                         DEBUG(5,("Could not obtain a name for SID %s\n",
262                                  sid_string_dbg(&s->user_sid)));
263                         request_error(s->state);
264                         return;                 
265                 }
266
267                 fstrcpy( username, user_name );         
268         }
269
270         strlower_m( username );
271         s->username = talloc_strdup(s->state->mem_ctx, username);
272
273         ws_name_replace( s->username, WB_REPLACE_CHAR );
274          
275         s->fullname = talloc_strdup(s->state->mem_ctx, full_name);
276         s->homedir = talloc_strdup(s->state->mem_ctx, homedir);
277         s->shell = talloc_strdup(s->state->mem_ctx, shell);
278         s->gid = gid;   
279         sid_copy(&s->group_sid, &s->domain->sid);
280         sid_append_rid(&s->group_sid, group_rid);
281
282         winbindd_sid2uid_async(s->state->mem_ctx, &s->user_sid,
283                                getpwsid_sid2uid_recv, s);
284 }
285
286 static void getpwsid_sid2uid_recv(void *private_data, bool success, uid_t uid)
287 {
288         struct getpwsid_state *s =
289                 talloc_get_type_abort(private_data, struct getpwsid_state);
290
291         if (!success) {
292                 DEBUG(5, ("Could not query uid for user %s\\%s\n",
293                           s->domain->name, s->username));
294                 request_error(s->state);
295                 return;
296         }
297
298         s->uid = uid;
299         winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
300                                getpwsid_sid2gid_recv, s);
301 }
302
303 static void getpwsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
304 {
305         struct getpwsid_state *s =
306                 talloc_get_type_abort(private_data, struct getpwsid_state);
307         struct winbindd_pw *pw;
308         fstring output_username;
309
310         /* allow the nss backend to override the primary group ID.
311            If the gid has already been set, then keep it.
312            This makes me feel dirty.  If the nss backend already
313            gave us a gid, we don't really care whether the sid2gid()
314            call worked or not.   --jerry  */
315
316         if ( s->gid == (gid_t)-1 ) {
317
318                 if (!success) {
319                         DEBUG(5, ("Could not query gid for user %s\\%s\n",
320                                   s->domain->name, s->username));
321                         goto failed;
322                 }
323
324                 /* take what the sid2gid() call gave us */
325                 s->gid = gid;
326         }
327
328         pw = &s->state->response.data.pw;
329         pw->pw_uid = s->uid;
330         pw->pw_gid = s->gid;
331         fill_domain_username(output_username, s->domain->name, s->username, True); 
332         safe_strcpy(pw->pw_name, output_username, sizeof(pw->pw_name) - 1);
333         safe_strcpy(pw->pw_gecos, s->fullname, sizeof(pw->pw_gecos) - 1);
334
335         if (!fillup_pw_field(lp_template_homedir(), s->username, s->domain->name, 
336                              pw->pw_uid, pw->pw_gid, s->homedir, pw->pw_dir)) {
337                 DEBUG(5, ("Could not compose homedir\n"));
338                 goto failed;
339         }
340
341         if (!fillup_pw_field(lp_template_shell(), s->username, s->domain->name, 
342                              pw->pw_uid, pw->pw_gid, s->shell, pw->pw_shell)) {
343                 DEBUG(5, ("Could not compose shell\n"));
344                 goto failed;
345         }
346
347         /* Password - set to "*" as we can't generate anything useful here.
348            Authentication can be done using the pam_winbind module. */
349
350         safe_strcpy(pw->pw_passwd, "*", sizeof(pw->pw_passwd) - 1);
351
352         request_ok(s->state);
353         return;
354
355  failed:
356         request_error(s->state);
357 }
358
359 /* Return a password structure from a username.  */
360
361 static void getpwnam_name2sid_recv(void *private_data, bool success,
362                                    const DOM_SID *sid, enum lsa_SidType type);
363
364 void winbindd_getpwnam(struct winbindd_cli_state *state)
365 {
366         struct winbindd_domain *domain;
367         fstring domname, username;
368
369         /* Ensure null termination */
370         state->request.data.username[sizeof(state->request.data.username)-1]='\0';
371
372         DEBUG(3, ("[%5lu]: getpwnam %s\n", (unsigned long)state->pid,
373                   state->request.data.username));
374
375         ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
376
377         if (!parse_domain_user(state->request.data.username, domname,
378                                username)) {
379                 DEBUG(5, ("Could not parse domain user: %s\n",
380                           state->request.data.username));
381                 request_error(state);
382                 return;
383         }
384         
385         /* Get info for the domain */
386
387         domain = find_domain_from_name(domname);
388
389         if (domain == NULL) {
390                 DEBUG(7, ("could not find domain entry for domain %s.  "
391                           "Using primary domain\n", domname));
392                 if ( (domain = find_our_domain()) == NULL ) {
393                         DEBUG(0,("Cannot find my primary domain structure!\n"));
394                 request_error(state);
395                 return;
396         }
397         }
398
399         if ( strequal(domname, lp_workgroup()) && lp_winbind_trusted_domains_only() ) {
400                 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getpwnam() for %s\\%s.\n", 
401                         domname, username));
402                 request_error(state);
403                 return;
404         }       
405
406         /* Get rid and name type from name.  The following costs 1 packet */
407
408         winbindd_lookupname_async(state->mem_ctx, domname, username,
409                                   getpwnam_name2sid_recv, WINBINDD_GETPWNAM, 
410                                   state);
411 }
412
413 static void getpwnam_name2sid_recv(void *private_data, bool success,
414                                    const DOM_SID *sid, enum lsa_SidType type)
415 {
416         struct winbindd_cli_state *state =
417                 (struct winbindd_cli_state *)private_data;
418         fstring domname, username;      
419
420         if (!success) {
421                 DEBUG(5, ("Could not lookup name for user %s\n",
422                           state->request.data.username));
423                 request_error(state);
424                 return;
425         }
426
427         if ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER)) {
428                 DEBUG(5, ("%s is not a user\n", state->request.data.username));
429                 request_error(state);
430                 return;
431         }
432
433         if ( parse_domain_user(state->request.data.username, domname, username) ) {
434                 check_domain_trusted( domname, sid );   
435         }
436
437
438
439         winbindd_getpwsid(state, sid);
440 }
441
442 static void getpwuid_recv(void *private_data, bool success, const char *sid)
443 {
444         struct winbindd_cli_state *state =
445                 (struct winbindd_cli_state *)private_data;
446         DOM_SID user_sid;
447
448         if (!success) {
449                 DEBUG(10,("uid2sid_recv: uid [%lu] to sid mapping failed\n.",
450                           (unsigned long)(state->request.data.uid)));
451                 request_error(state);
452                 return;
453         }
454         
455         DEBUG(10,("uid2sid_recv: uid %lu has sid %s\n",
456                   (unsigned long)(state->request.data.uid), sid));
457
458         string_to_sid(&user_sid, sid);
459         winbindd_getpwsid(state, &user_sid);
460 }
461
462 /* Return a password structure given a uid number */
463 void winbindd_getpwuid(struct winbindd_cli_state *state)
464 {
465         DEBUG(3, ("[%5lu]: getpwuid %lu\n", (unsigned long)state->pid, 
466                   (unsigned long)state->request.data.uid));
467
468         /* always query idmap via the async interface */
469         /* if this turns to be too slow we will add here a direct query to the cache */
470         winbindd_uid2sid_async(state->mem_ctx, state->request.data.uid, getpwuid_recv, state);
471 }
472
473 /*
474  * set/get/endpwent functions
475  */
476
477 /* Rewind file pointer for ntdom passwd database */
478
479 static bool winbindd_setpwent_internal(struct winbindd_cli_state *state)
480 {
481         struct winbindd_domain *domain;
482         
483         DEBUG(3, ("[%5lu]: setpwent\n", (unsigned long)state->pid));
484         
485         /* Check user has enabled this */
486         
487         if (!lp_winbind_enum_users()) {
488                 return False;
489         }
490
491         /* Free old static data if it exists */
492         
493         if (state->getpwent_state != NULL) {
494                 free_getent_state(state->getpwent_state);
495                 state->getpwent_state = NULL;
496         }
497
498 #if 0   /* JERRY */
499         /* add any local users we have */
500                 
501         if ( (domain_state = (struct getent_state *)malloc(sizeof(struct getent_state))) == NULL )
502                 return False;
503                 
504         ZERO_STRUCTP(domain_state);
505
506         /* Add to list of open domains */
507                 
508         DLIST_ADD(state->getpwent_state, domain_state);
509 #endif
510         
511         /* Create sam pipes for each domain we know about */
512         
513         for(domain = domain_list(); domain != NULL; domain = domain->next) {
514                 struct getent_state *domain_state;
515                 
516                 
517                 /* don't add our domaina if we are a PDC or if we 
518                    are a member of a Samba domain */
519                 
520                 if ( (IS_DC || lp_winbind_trusted_domains_only())
521                         && strequal(domain->name, lp_workgroup()) )
522                 {
523                         continue;
524                 }
525                                                 
526                 /* Create a state record for this domain */
527                 
528                 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
529                         DEBUG(0, ("malloc failed\n"));
530                         return False;
531                 }
532                 
533                 ZERO_STRUCTP(domain_state);
534
535                 fstrcpy(domain_state->domain_name, domain->name);
536
537                 /* Add to list of open domains */
538                 
539                 DLIST_ADD(state->getpwent_state, domain_state);
540         }
541         
542         state->getpwent_initialized = True;
543         return True;
544 }
545
546 void winbindd_setpwent(struct winbindd_cli_state *state)
547 {
548         if (winbindd_setpwent_internal(state)) {
549                 request_ok(state);
550         } else {
551                 request_error(state);
552         }
553 }
554
555 /* Close file pointer to ntdom passwd database */
556
557 void winbindd_endpwent(struct winbindd_cli_state *state)
558 {
559         DEBUG(3, ("[%5lu]: endpwent\n", (unsigned long)state->pid));
560
561         free_getent_state(state->getpwent_state);    
562         state->getpwent_initialized = False;
563         state->getpwent_state = NULL;
564         request_ok(state);
565 }
566
567 /* Get partial list of domain users for a domain.  We fill in the sam_entries,
568    and num_sam_entries fields with domain user information.  The dispinfo_ndx
569    field is incremented to the index of the next user to fetch.  Return True if
570    some users were returned, False otherwise. */
571
572 static bool get_sam_user_entries(struct getent_state *ent, TALLOC_CTX *mem_ctx)
573 {
574         NTSTATUS status;
575         uint32 num_entries;
576         WINBIND_USERINFO *info;
577         struct getpwent_user *name_list = NULL;
578         struct winbindd_domain *domain;
579         struct winbindd_methods *methods;
580         unsigned int i;
581
582         if (ent->num_sam_entries)
583                 return False;
584
585         if (!(domain = find_domain_from_name(ent->domain_name))) {
586                 DEBUG(3, ("no such domain %s in get_sam_user_entries\n",
587                           ent->domain_name));
588                 return False;
589         }
590
591         methods = domain->methods;
592
593         /* Free any existing user info */
594
595         SAFE_FREE(ent->sam_entries);
596         ent->num_sam_entries = 0;
597         
598         /* Call query_user_list to get a list of usernames and user rids */
599
600         num_entries = 0;
601
602         status = methods->query_user_list(domain, mem_ctx, &num_entries, 
603                                           &info);
604                 
605         if (!NT_STATUS_IS_OK(status)) {
606                 DEBUG(10,("get_sam_user_entries: query_user_list failed with %s\n",
607                         nt_errstr(status) ));
608                 return False;
609         }
610
611         if (num_entries) {
612                 name_list = SMB_REALLOC_ARRAY(name_list, struct getpwent_user, ent->num_sam_entries + num_entries);
613                 
614                 if (!name_list) {
615                         DEBUG(0,("get_sam_user_entries realloc failed.\n"));
616                         return False;
617                 }
618         }
619
620         for (i = 0; i < num_entries; i++) {
621                 /* Store account name and gecos */
622                 if (!info[i].acct_name) {
623                         fstrcpy(name_list[ent->num_sam_entries + i].name, "");
624                 } else {
625                         fstrcpy(name_list[ent->num_sam_entries + i].name, 
626                                 info[i].acct_name); 
627                 }
628                 if (!info[i].full_name) {
629                         fstrcpy(name_list[ent->num_sam_entries + i].gecos, "");
630                 } else {
631                         fstrcpy(name_list[ent->num_sam_entries + i].gecos, 
632                                 info[i].full_name); 
633                 }
634                 if (!info[i].homedir) {
635                         fstrcpy(name_list[ent->num_sam_entries + i].homedir, "");
636                 } else {
637                         fstrcpy(name_list[ent->num_sam_entries + i].homedir, 
638                                 info[i].homedir); 
639                 }
640                 if (!info[i].shell) {
641                         fstrcpy(name_list[ent->num_sam_entries + i].shell, "");
642                 } else {
643                         fstrcpy(name_list[ent->num_sam_entries + i].shell, 
644                                 info[i].shell); 
645                 }
646         
647         
648                 /* User and group ids */
649                 sid_copy(&name_list[ent->num_sam_entries+i].user_sid,
650                          &info[i].user_sid);
651                 sid_copy(&name_list[ent->num_sam_entries+i].group_sid,
652                          &info[i].group_sid);
653         }
654                 
655         ent->num_sam_entries += num_entries;
656         
657         /* Fill in remaining fields */
658         
659         ent->sam_entries = name_list;
660         ent->sam_entry_index = 0;
661         return ent->num_sam_entries > 0;
662 }
663
664 /* Fetch next passwd entry from ntdom database */
665
666 #define MAX_GETPWENT_USERS 500
667
668 void winbindd_getpwent(struct winbindd_cli_state *state)
669 {
670         struct getent_state *ent;
671         struct winbindd_pw *user_list;
672         int num_users, user_list_ndx;
673
674         DEBUG(3, ("[%5lu]: getpwent\n", (unsigned long)state->pid));
675
676         /* Check user has enabled this */
677
678         if (!lp_winbind_enum_users()) {
679                 request_error(state);
680                 return;
681         }
682
683         /* Allocate space for returning a chunk of users */
684
685         num_users = MIN(MAX_GETPWENT_USERS, state->request.data.num_entries);
686
687         if (num_users == 0) {
688                 request_error(state);
689                 return;
690         }
691         
692         if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_pw, num_users)) == NULL) {
693                 request_error(state);
694                 return;
695         }
696
697         memset(state->response.extra_data.data, 0, num_users * 
698                sizeof(struct winbindd_pw));
699
700         user_list = (struct winbindd_pw *)state->response.extra_data.data;
701
702         if (!state->getpwent_initialized)
703                 winbindd_setpwent_internal(state);
704         
705         if (!(ent = state->getpwent_state)) {
706                 request_error(state);
707                 return;
708         }
709
710         /* Start sending back users */
711
712         for (user_list_ndx = 0; user_list_ndx < num_users; ) {
713                 struct getpwent_user *name_list = NULL;
714                 uint32 result;
715
716                 /* Do we need to fetch another chunk of users? */
717
718                 if (ent->num_sam_entries == ent->sam_entry_index) {
719
720                         while(ent &&
721                               !get_sam_user_entries(ent, state->mem_ctx)) {
722                                 struct getent_state *next_ent;
723
724                                 /* Free state information for this domain */
725
726                                 SAFE_FREE(ent->sam_entries);
727
728                                 next_ent = ent->next;
729                                 DLIST_REMOVE(state->getpwent_state, ent);
730
731                                 SAFE_FREE(ent);
732                                 ent = next_ent;
733                         }
734  
735                         /* No more domains */
736
737                         if (!ent) 
738                                 break;
739                 }
740
741                 name_list = (struct getpwent_user *)ent->sam_entries;
742
743                 /* Lookup user info */
744                 
745                 result = winbindd_fill_pwent(
746                         ent->domain_name, 
747                         name_list[ent->sam_entry_index].name,
748                         &name_list[ent->sam_entry_index].user_sid,
749                         &name_list[ent->sam_entry_index].group_sid,
750                         name_list[ent->sam_entry_index].gecos,
751                         name_list[ent->sam_entry_index].homedir,
752                         name_list[ent->sam_entry_index].shell,
753                         &user_list[user_list_ndx]);
754                 
755                 /* Add user to return list */
756                 
757                 if (result) {
758                                 
759                         user_list_ndx++;
760                         state->response.data.num_entries++;
761                         state->response.length += 
762                                 sizeof(struct winbindd_pw);
763
764                 } else
765                         DEBUG(1, ("could not lookup domain user %s\n",
766                                   name_list[ent->sam_entry_index].name));
767
768                 ent->sam_entry_index++;
769                 
770         }
771
772         /* Out of domains */
773
774         if (user_list_ndx > 0)
775                 request_ok(state);
776         else
777                 request_error(state);
778 }
779
780 /* List domain users without mapping to unix ids */
781
782 void winbindd_list_users(struct winbindd_cli_state *state)
783 {
784         struct winbindd_domain *domain;
785         WINBIND_USERINFO *info;
786         const char *which_domain;
787         uint32 num_entries = 0, total_entries = 0;
788         char *extra_data = NULL;
789         int extra_data_len = 0;
790         enum winbindd_result rv = WINBINDD_ERROR;
791
792         DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid));
793
794         /* Ensure null termination */
795         state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';  
796         which_domain = state->request.domain_name;
797         
798         /* Enumerate over trusted domains */
799
800         for (domain = domain_list(); domain; domain = domain->next) {
801                 NTSTATUS status;
802                 struct winbindd_methods *methods;
803                 unsigned int i;
804                 
805                 /* if we have a domain name restricting the request and this
806                    one in the list doesn't match, then just bypass the remainder
807                    of the loop */
808                    
809                 if ( *which_domain && !strequal(which_domain, domain->name) )
810                         continue;
811                         
812                 methods = domain->methods;
813
814                 /* Query display info */
815                 status = methods->query_user_list(domain, state->mem_ctx, 
816                                                   &num_entries, &info);
817
818                 if (!NT_STATUS_IS_OK(status)) {
819                         continue;
820                 }
821
822                 if (num_entries == 0)
823                         continue;
824
825                 /* Allocate some memory for extra data */
826                 total_entries += num_entries;
827                         
828                 extra_data = (char *)SMB_REALLOC(
829                         extra_data, sizeof(fstring) * total_entries);
830                         
831                 if (!extra_data) {
832                         DEBUG(0,("failed to enlarge buffer!\n"));
833                         goto done;
834                 }
835
836                 /* Pack user list into extra data fields */
837                         
838                 for (i = 0; i < num_entries; i++) {
839                         fstring acct_name, name;
840                         
841                         if (!info[i].acct_name) {
842                                 fstrcpy(acct_name, "");
843                         } else {
844                                 fstrcpy(acct_name, info[i].acct_name);
845                         }
846                         
847                         fill_domain_username(name, domain->name, acct_name, True);
848                         
849                                 /* Append to extra data */
850                         memcpy(&extra_data[extra_data_len], name, 
851                                strlen(name));
852                         extra_data_len += strlen(name);
853                         extra_data[extra_data_len++] = ',';
854                 }   
855         }
856
857         /* Assign extra_data fields in response structure */
858
859         if (extra_data) {
860                 extra_data[extra_data_len - 1] = '\0';
861                 state->response.extra_data.data = extra_data;
862                 state->response.length += extra_data_len;
863         }
864
865         /* No domains responded but that's still OK so don't return an
866            error. */
867
868         rv = WINBINDD_OK;
869
870  done:
871
872         if (rv == WINBINDD_OK)
873                 request_ok(state);
874         else
875                 request_error(state);
876 }