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