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