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